texture_cache: Implement rendering to 3D textures
This allows rendering to 3D textures with more than one slice. Applications are allowed to render to more than one slice of a texture using gl_Layer from a VTG shader. This also requires reworking how 3D texture collisions are handled, for now, this commit allows rendering to slices but not to miplevels. When a render target attempts to write to a mipmap, we fallback to the previous implementation (copying or flushing as needed). - Fixes color correction 3D textures on UE4 games (rainbow effects). - Allows Xenoblade games to render to 3D textures directly.
This commit is contained in:
parent
2293e8a11a
commit
c95c254f3e
10 changed files with 196 additions and 144 deletions
|
@ -598,6 +598,7 @@ public:
|
|||
BitField<4, 3, u32> block_height;
|
||||
BitField<8, 3, u32> block_depth;
|
||||
BitField<12, 1, InvMemoryLayout> type;
|
||||
BitField<16, 1, u32> is_3d;
|
||||
} memory_layout;
|
||||
union {
|
||||
BitField<0, 16, u32> layers;
|
||||
|
|
|
@ -263,9 +263,14 @@ CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& param
|
|||
target = GetTextureTarget(params.target);
|
||||
texture = CreateTexture(params, target, internal_format, texture_buffer);
|
||||
DecorateSurfaceName();
|
||||
main_view = CreateViewInner(
|
||||
ViewParams(params.target, 0, params.is_layered ? params.depth : 1, 0, params.num_levels),
|
||||
true);
|
||||
|
||||
u32 num_layers = 1;
|
||||
if (params.is_layered || params.target == SurfaceTarget::Texture3D) {
|
||||
num_layers = params.depth;
|
||||
}
|
||||
|
||||
main_view =
|
||||
CreateViewInner(ViewParams(params.target, 0, num_layers, 0, params.num_levels), true);
|
||||
}
|
||||
|
||||
CachedSurface::~CachedSurface() = default;
|
||||
|
@ -413,37 +418,40 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
|
|||
|
||||
CachedSurfaceView::~CachedSurfaceView() = default;
|
||||
|
||||
void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const {
|
||||
void CachedSurfaceView::Attach(GLenum attachment, GLenum fb_target) const {
|
||||
ASSERT(params.num_levels == 1);
|
||||
|
||||
if (params.num_layers > 1) {
|
||||
// Layered framebuffer attachments
|
||||
UNIMPLEMENTED_IF(params.base_layer != 0);
|
||||
|
||||
switch (params.target) {
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
glFramebufferTexture(target, attachment, GetTexture(), 0);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
if (params.target == SurfaceTarget::Texture3D) {
|
||||
if (params.num_layers > 1) {
|
||||
ASSERT(params.base_layer == 0);
|
||||
glFramebufferTexture(fb_target, attachment, surface.texture.handle, params.base_level);
|
||||
} else {
|
||||
glFramebufferTexture3D(fb_target, attachment, target, surface.texture.handle,
|
||||
params.base_level, params.base_layer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.num_layers > 1) {
|
||||
UNIMPLEMENTED_IF(params.base_layer != 0);
|
||||
glFramebufferTexture(fb_target, attachment, GetTexture(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const GLenum view_target = surface.GetTarget();
|
||||
const GLuint texture = surface.GetTexture();
|
||||
switch (surface.GetSurfaceParams().target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level);
|
||||
glFramebufferTexture1D(fb_target, attachment, view_target, texture, params.base_level);
|
||||
break;
|
||||
case SurfaceTarget::Texture2D:
|
||||
glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level);
|
||||
glFramebufferTexture2D(fb_target, attachment, view_target, texture, params.base_level);
|
||||
break;
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
glFramebufferTextureLayer(target, attachment, texture, params.base_level,
|
||||
glFramebufferTextureLayer(fb_target, attachment, texture, params.base_level,
|
||||
params.base_layer);
|
||||
break;
|
||||
default:
|
||||
|
@ -500,8 +508,13 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
|||
OGLTextureView texture_view;
|
||||
texture_view.Create();
|
||||
|
||||
glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level,
|
||||
params.num_levels, params.base_layer, params.num_layers);
|
||||
if (target == GL_TEXTURE_3D) {
|
||||
glTextureView(texture_view.handle, target, surface.texture.handle, format,
|
||||
params.base_level, params.num_levels, 0, 1);
|
||||
} else {
|
||||
glTextureView(texture_view.handle, target, surface.texture.handle, format,
|
||||
params.base_level, params.num_levels, params.base_layer, params.num_layers);
|
||||
}
|
||||
ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle);
|
||||
|
||||
return texture_view;
|
||||
|
|
|
@ -80,8 +80,10 @@ public:
|
|||
explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy);
|
||||
~CachedSurfaceView();
|
||||
|
||||
/// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
|
||||
void Attach(GLenum attachment, GLenum target) const;
|
||||
/// @brief Attaches this texture view to the currently bound fb_target framebuffer
|
||||
/// @param attachment Attachment to bind textures to
|
||||
/// @param fb_target Framebuffer target to attach to (e.g. DRAW_FRAMEBUFFER)
|
||||
void Attach(GLenum attachment, GLenum fb_target) const;
|
||||
|
||||
GLuint GetTexture(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
|
|
|
@ -716,7 +716,7 @@ std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers(
|
|||
if (!view) {
|
||||
return false;
|
||||
}
|
||||
key.views.push_back(view->GetHandle());
|
||||
key.views.push_back(view->GetAttachment());
|
||||
key.width = std::min(key.width, view->GetWidth());
|
||||
key.height = std::min(key.height, view->GetHeight());
|
||||
key.layers = std::min(key.layers, view->GetNumLayers());
|
||||
|
@ -1137,8 +1137,8 @@ void RasterizerVulkan::SetupTexture(const Tegra::Texture::FullTextureInfo& textu
|
|||
auto view = texture_cache.GetTextureSurface(texture.tic, entry);
|
||||
ASSERT(!view->IsBufferView());
|
||||
|
||||
const auto image_view = view->GetHandle(texture.tic.x_source, texture.tic.y_source,
|
||||
texture.tic.z_source, texture.tic.w_source);
|
||||
const VkImageView image_view = view->GetImageView(texture.tic.x_source, texture.tic.y_source,
|
||||
texture.tic.z_source, texture.tic.w_source);
|
||||
const auto sampler = sampler_cache.GetSampler(texture.tsc);
|
||||
update_descriptor_queue.AddSampledImage(sampler, image_view);
|
||||
|
||||
|
@ -1164,7 +1164,8 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
|
|||
|
||||
UNIMPLEMENTED_IF(tic.IsBuffer());
|
||||
|
||||
const auto image_view = view->GetHandle(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
|
||||
const VkImageView image_view =
|
||||
view->GetImageView(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
|
||||
update_descriptor_queue.AddImage(image_view);
|
||||
|
||||
const auto image_layout = update_descriptor_queue.GetLastImageLayout();
|
||||
|
|
|
@ -167,6 +167,7 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP
|
|||
ci.extent = {params.width, params.height, 1};
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
ci.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||
ci.extent = {params.width, params.height, params.depth};
|
||||
break;
|
||||
case SurfaceTarget::TextureBuffer:
|
||||
|
@ -176,6 +177,12 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP
|
|||
return ci;
|
||||
}
|
||||
|
||||
u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::SwizzleSource y_source,
|
||||
Tegra::Texture::SwizzleSource z_source, Tegra::Texture::SwizzleSource w_source) {
|
||||
return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
|
||||
(static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
|
||||
|
@ -203,9 +210,11 @@ CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
|
|||
}
|
||||
|
||||
// TODO(Rodrigo): Move this to a virtual function.
|
||||
main_view = CreateViewInner(
|
||||
ViewParams(params.target, 0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels),
|
||||
true);
|
||||
u32 num_layers = 1;
|
||||
if (params.is_layered || params.target == SurfaceTarget::Texture3D) {
|
||||
num_layers = params.depth;
|
||||
}
|
||||
main_view = CreateView(ViewParams(params.target, 0, num_layers, 0, params.num_levels));
|
||||
}
|
||||
|
||||
CachedSurface::~CachedSurface() = default;
|
||||
|
@ -253,12 +262,8 @@ void CachedSurface::DecorateSurfaceName() {
|
|||
}
|
||||
|
||||
View CachedSurface::CreateView(const ViewParams& params) {
|
||||
return CreateViewInner(params, false);
|
||||
}
|
||||
|
||||
View CachedSurface::CreateViewInner(const ViewParams& params, bool is_proxy) {
|
||||
// TODO(Rodrigo): Add name decorations
|
||||
return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params, is_proxy);
|
||||
return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params);
|
||||
}
|
||||
|
||||
void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {
|
||||
|
@ -342,18 +347,27 @@ VkImageSubresourceRange CachedSurface::GetImageSubresourceRange() const {
|
|||
}
|
||||
|
||||
CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
|
||||
const ViewParams& params, bool is_proxy)
|
||||
const ViewParams& params)
|
||||
: VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()},
|
||||
image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()},
|
||||
aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface},
|
||||
base_layer{params.base_layer}, num_layers{params.num_layers}, base_level{params.base_level},
|
||||
num_levels{params.num_levels}, image_view_type{image ? GetImageViewType(params.target)
|
||||
: VK_IMAGE_VIEW_TYPE_1D} {}
|
||||
base_level{params.base_level}, num_levels{params.num_levels},
|
||||
image_view_type{image ? GetImageViewType(params.target) : VK_IMAGE_VIEW_TYPE_1D} {
|
||||
if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
|
||||
base_layer = 0;
|
||||
num_layers = 1;
|
||||
base_slice = params.base_layer;
|
||||
num_slices = params.num_layers;
|
||||
} else {
|
||||
base_layer = params.base_layer;
|
||||
num_layers = params.num_layers;
|
||||
}
|
||||
}
|
||||
|
||||
CachedSurfaceView::~CachedSurfaceView() = default;
|
||||
|
||||
VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source,
|
||||
SwizzleSource z_source, SwizzleSource w_source) {
|
||||
VkImageView CachedSurfaceView::GetImageView(SwizzleSource x_source, SwizzleSource y_source,
|
||||
SwizzleSource z_source, SwizzleSource w_source) {
|
||||
const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
|
||||
if (last_image_view && last_swizzle == new_swizzle) {
|
||||
return last_image_view;
|
||||
|
@ -399,6 +413,11 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
|
|||
});
|
||||
}
|
||||
|
||||
if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
|
||||
ASSERT(base_slice == 0);
|
||||
ASSERT(num_slices == params.depth);
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
|
@ -417,6 +436,35 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
|
|||
return last_image_view = *image_view;
|
||||
}
|
||||
|
||||
VkImageView CachedSurfaceView::GetAttachment() {
|
||||
if (render_target) {
|
||||
return *render_target;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.image = surface.GetImageHandle();
|
||||
ci.format = surface.GetImage().GetFormat();
|
||||
ci.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
ci.subresourceRange.aspectMask = aspect_mask;
|
||||
ci.subresourceRange.baseMipLevel = base_level;
|
||||
ci.subresourceRange.levelCount = num_levels;
|
||||
if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
|
||||
ci.viewType = num_slices > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
|
||||
ci.subresourceRange.baseArrayLayer = base_slice;
|
||||
ci.subresourceRange.layerCount = num_slices;
|
||||
} else {
|
||||
ci.viewType = image_view_type;
|
||||
ci.subresourceRange.baseArrayLayer = base_layer;
|
||||
ci.subresourceRange.layerCount = num_layers;
|
||||
}
|
||||
render_target = device.GetLogical().CreateImageView(ci);
|
||||
return *render_target;
|
||||
}
|
||||
|
||||
VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||
const VKDevice& device, VKResourceManager& resource_manager,
|
||||
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
||||
|
|
|
@ -91,7 +91,6 @@ protected:
|
|||
void DecorateSurfaceName();
|
||||
|
||||
View CreateView(const ViewParams& params) override;
|
||||
View CreateViewInner(const ViewParams& params, bool is_proxy);
|
||||
|
||||
private:
|
||||
void UploadBuffer(const std::vector<u8>& staging_buffer);
|
||||
|
@ -120,23 +119,20 @@ private:
|
|||
class CachedSurfaceView final : public VideoCommon::ViewBase {
|
||||
public:
|
||||
explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
|
||||
const ViewParams& params, bool is_proxy);
|
||||
const ViewParams& params);
|
||||
~CachedSurfaceView();
|
||||
|
||||
VkImageView GetHandle(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
Tegra::Texture::SwizzleSource z_source,
|
||||
Tegra::Texture::SwizzleSource w_source);
|
||||
VkImageView GetImageView(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
Tegra::Texture::SwizzleSource z_source,
|
||||
Tegra::Texture::SwizzleSource w_source);
|
||||
|
||||
VkImageView GetAttachment();
|
||||
|
||||
bool IsSameSurface(const CachedSurfaceView& rhs) const {
|
||||
return &surface == &rhs.surface;
|
||||
}
|
||||
|
||||
VkImageView GetHandle() {
|
||||
return GetHandle(Tegra::Texture::SwizzleSource::R, Tegra::Texture::SwizzleSource::G,
|
||||
Tegra::Texture::SwizzleSource::B, Tegra::Texture::SwizzleSource::A);
|
||||
}
|
||||
|
||||
u32 GetWidth() const {
|
||||
return params.GetMipWidth(base_level);
|
||||
}
|
||||
|
@ -180,14 +176,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
static u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
Tegra::Texture::SwizzleSource z_source,
|
||||
Tegra::Texture::SwizzleSource w_source) {
|
||||
return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
|
||||
(static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
|
||||
}
|
||||
|
||||
// Store a copy of these values to avoid double dereference when reading them
|
||||
const SurfaceParams params;
|
||||
const VkImage image;
|
||||
|
@ -196,15 +184,18 @@ private:
|
|||
|
||||
const VKDevice& device;
|
||||
CachedSurface& surface;
|
||||
const u32 base_layer;
|
||||
const u32 num_layers;
|
||||
const u32 base_level;
|
||||
const u32 num_levels;
|
||||
const VkImageViewType image_view_type;
|
||||
u32 base_layer = 0;
|
||||
u32 num_layers = 0;
|
||||
u32 base_slice = 0;
|
||||
u32 num_slices = 0;
|
||||
|
||||
VkImageView last_image_view = nullptr;
|
||||
u32 last_swizzle = 0;
|
||||
|
||||
vk::ImageView render_target;
|
||||
std::unordered_map<u32, vk::ImageView> view_cache;
|
||||
};
|
||||
|
||||
|
|
|
@ -248,12 +248,11 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
|||
|
||||
// Use an extra temporal buffer
|
||||
auto& tmp_buffer = staging_cache.GetBuffer(1);
|
||||
// Special case for 3D Texture Segments
|
||||
const bool must_read_current_data =
|
||||
params.block_depth > 0 && params.target == VideoCore::Surface::SurfaceTarget::Texture2D;
|
||||
tmp_buffer.resize(guest_memory_size);
|
||||
host_ptr = tmp_buffer.data();
|
||||
if (must_read_current_data) {
|
||||
|
||||
if (params.target == SurfaceTarget::Texture3D) {
|
||||
// Special case for 3D texture segments
|
||||
memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
|
||||
}
|
||||
|
||||
|
|
|
@ -217,8 +217,8 @@ public:
|
|||
}
|
||||
|
||||
bool IsProtected() const {
|
||||
// Only 3D Slices are to be protected
|
||||
return is_target && params.block_depth > 0;
|
||||
// Only 3D slices are to be protected
|
||||
return is_target && params.target == SurfaceTarget::Texture3D;
|
||||
}
|
||||
|
||||
bool IsRenderTarget() const {
|
||||
|
@ -250,6 +250,11 @@ public:
|
|||
return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels));
|
||||
}
|
||||
|
||||
TView Emplace3DView(u32 slice, u32 depth, u32 base_level, u32 num_levels) {
|
||||
return GetView(ViewParams(VideoCore::Surface::SurfaceTarget::Texture3D, slice, depth,
|
||||
base_level, num_levels));
|
||||
}
|
||||
|
||||
std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params,
|
||||
const GPUVAddr view_addr,
|
||||
const std::size_t candidate_size, const u32 mipmap,
|
||||
|
@ -272,8 +277,8 @@ public:
|
|||
std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr,
|
||||
const std::size_t candidate_size) {
|
||||
if (params.target == SurfaceTarget::Texture3D ||
|
||||
(params.num_levels == 1 && !params.is_layered) ||
|
||||
view_params.target == SurfaceTarget::Texture3D) {
|
||||
view_params.target == SurfaceTarget::Texture3D ||
|
||||
(params.num_levels == 1 && !params.is_layered)) {
|
||||
return {};
|
||||
}
|
||||
const auto layer_mipmap{GetLayerMipmap(view_addr)};
|
||||
|
|
|
@ -215,10 +215,19 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
|
|||
params.num_levels = 1;
|
||||
params.emulated_levels = 1;
|
||||
|
||||
const bool is_layered = config.layers > 1 && params.block_depth == 0;
|
||||
params.is_layered = is_layered;
|
||||
params.depth = is_layered ? config.layers.Value() : 1;
|
||||
params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
|
||||
if (config.memory_layout.is_3d != 0) {
|
||||
params.depth = config.layers.Value();
|
||||
params.is_layered = false;
|
||||
params.target = SurfaceTarget::Texture3D;
|
||||
} else if (config.layers > 1) {
|
||||
params.depth = config.layers.Value();
|
||||
params.is_layered = true;
|
||||
params.target = SurfaceTarget::Texture2DArray;
|
||||
} else {
|
||||
params.depth = 1;
|
||||
params.is_layered = false;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
|
|
|
@ -508,12 +508,12 @@ private:
|
|||
return RecycleStrategy::Flush;
|
||||
}
|
||||
// 3D Textures decision
|
||||
if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) {
|
||||
if (params.target == SurfaceTarget::Texture3D) {
|
||||
return RecycleStrategy::Flush;
|
||||
}
|
||||
for (const auto& s : overlaps) {
|
||||
const auto& s_params = s->GetSurfaceParams();
|
||||
if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) {
|
||||
if (s_params.target == SurfaceTarget::Texture3D) {
|
||||
return RecycleStrategy::Flush;
|
||||
}
|
||||
}
|
||||
|
@ -726,76 +726,60 @@ private:
|
|||
* @param params The parameters on the new surface.
|
||||
* @param gpu_addr The starting address of the new surface.
|
||||
* @param cpu_addr The starting address of the new surface on physical memory.
|
||||
* @param preserve_contents Indicates that the new surface should be loaded from memory or
|
||||
* left blank.
|
||||
*/
|
||||
std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(VectorSurface& overlaps,
|
||||
const SurfaceParams& params,
|
||||
const GPUVAddr gpu_addr,
|
||||
const VAddr cpu_addr,
|
||||
bool preserve_contents) {
|
||||
if (params.target == SurfaceTarget::Texture3D) {
|
||||
bool failed = false;
|
||||
if (params.num_levels > 1) {
|
||||
// We can't handle mipmaps in 3D textures yet, better fallback to LLE approach
|
||||
return std::nullopt;
|
||||
}
|
||||
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
|
||||
bool modified = false;
|
||||
for (auto& surface : overlaps) {
|
||||
const SurfaceParams& src_params = surface->GetSurfaceParams();
|
||||
if (src_params.target != SurfaceTarget::Texture2D) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
if (src_params.height != params.height) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
if (src_params.block_depth != params.block_depth ||
|
||||
src_params.block_height != params.block_height) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr);
|
||||
const auto offsets = params.GetBlockOffsetXYZ(offset);
|
||||
const auto z = std::get<2>(offsets);
|
||||
modified |= surface->IsModified();
|
||||
const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height,
|
||||
1);
|
||||
ImageCopy(surface, new_surface, copy_params);
|
||||
}
|
||||
if (failed) {
|
||||
return std::nullopt;
|
||||
}
|
||||
for (const auto& surface : overlaps) {
|
||||
Unregister(surface);
|
||||
}
|
||||
new_surface->MarkAsModified(modified, Tick());
|
||||
Register(new_surface);
|
||||
auto view = new_surface->GetMainView();
|
||||
return {{std::move(new_surface), view}};
|
||||
} else {
|
||||
for (const auto& surface : overlaps) {
|
||||
if (!surface->MatchTarget(params.target)) {
|
||||
if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) {
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Unregister(surface);
|
||||
return InitializeSurface(gpu_addr, params, preserve_contents);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
if (surface->GetCpuAddr() != cpu_addr) {
|
||||
continue;
|
||||
}
|
||||
if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) {
|
||||
return {{surface, surface->GetMainView()}};
|
||||
}
|
||||
}
|
||||
return InitializeSurface(gpu_addr, params, preserve_contents);
|
||||
GPUVAddr gpu_addr, VAddr cpu_addr) {
|
||||
if (params.num_levels > 1) {
|
||||
// We can't handle mipmaps in 3D textures yet, better fallback to LLE approach
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (overlaps.size() == 1) {
|
||||
const auto& surface = overlaps[0];
|
||||
const SurfaceParams& overlap_params = surface->GetSurfaceParams();
|
||||
// Don't attempt to render to textures with more than one level for now
|
||||
// The texture has to be to the right or the sample address if we want to render to it
|
||||
if (overlap_params.num_levels == 1 && cpu_addr >= surface->GetCpuAddr()) {
|
||||
const u32 offset = static_cast<u32>(cpu_addr - surface->GetCpuAddr());
|
||||
const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset));
|
||||
if (slice < overlap_params.depth) {
|
||||
auto view = surface->Emplace3DView(slice, params.depth, 0, 1);
|
||||
return std::make_pair(std::move(surface), std::move(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.depth == 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
|
||||
bool modified = false;
|
||||
for (auto& surface : overlaps) {
|
||||
const SurfaceParams& src_params = surface->GetSurfaceParams();
|
||||
if (src_params.height != params.height ||
|
||||
src_params.block_depth != params.block_depth ||
|
||||
src_params.block_height != params.block_height) {
|
||||
return std::nullopt;
|
||||
}
|
||||
modified |= surface->IsModified();
|
||||
|
||||
const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr);
|
||||
const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset));
|
||||
const u32 width = params.width;
|
||||
const u32 height = params.height;
|
||||
const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1);
|
||||
ImageCopy(surface, new_surface, copy_params);
|
||||
}
|
||||
for (const auto& surface : overlaps) {
|
||||
Unregister(surface);
|
||||
}
|
||||
new_surface->MarkAsModified(modified, Tick());
|
||||
Register(new_surface);
|
||||
|
||||
auto view = new_surface->GetMainView();
|
||||
return std::make_pair(std::move(new_surface), std::move(view));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -873,10 +857,9 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
// Check if it's a 3D texture
|
||||
if (params.block_depth > 0) {
|
||||
auto surface =
|
||||
Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents);
|
||||
// Manage 3D textures
|
||||
if (params.target == SurfaceTarget::Texture3D) {
|
||||
auto surface = Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr);
|
||||
if (surface) {
|
||||
return *surface;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue