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
|
@ -598,6 +598,7 @@ public:
|
||||||
BitField<4, 3, u32> block_height;
|
BitField<4, 3, u32> block_height;
|
||||||
BitField<8, 3, u32> block_depth;
|
BitField<8, 3, u32> block_depth;
|
||||||
BitField<12, 1, InvMemoryLayout> type;
|
BitField<12, 1, InvMemoryLayout> type;
|
||||||
|
BitField<16, 1, u32> is_3d;
|
||||||
} memory_layout;
|
} memory_layout;
|
||||||
union {
|
union {
|
||||||
BitField<0, 16, u32> layers;
|
BitField<0, 16, u32> layers;
|
||||||
|
|
|
@ -263,9 +263,14 @@ CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& param
|
||||||
target = GetTextureTarget(params.target);
|
target = GetTextureTarget(params.target);
|
||||||
texture = CreateTexture(params, target, internal_format, texture_buffer);
|
texture = CreateTexture(params, target, internal_format, texture_buffer);
|
||||||
DecorateSurfaceName();
|
DecorateSurfaceName();
|
||||||
main_view = CreateViewInner(
|
|
||||||
ViewParams(params.target, 0, params.is_layered ? params.depth : 1, 0, params.num_levels),
|
u32 num_layers = 1;
|
||||||
true);
|
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;
|
CachedSurface::~CachedSurface() = default;
|
||||||
|
@ -413,37 +418,40 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
|
||||||
|
|
||||||
CachedSurfaceView::~CachedSurfaceView() = default;
|
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);
|
ASSERT(params.num_levels == 1);
|
||||||
|
|
||||||
if (params.num_layers > 1) {
|
if (params.target == SurfaceTarget::Texture3D) {
|
||||||
// Layered framebuffer attachments
|
if (params.num_layers > 1) {
|
||||||
UNIMPLEMENTED_IF(params.base_layer != 0);
|
ASSERT(params.base_layer == 0);
|
||||||
|
glFramebufferTexture(fb_target, attachment, surface.texture.handle, params.base_level);
|
||||||
switch (params.target) {
|
} else {
|
||||||
case SurfaceTarget::Texture2DArray:
|
glFramebufferTexture3D(fb_target, attachment, target, surface.texture.handle,
|
||||||
glFramebufferTexture(target, attachment, GetTexture(), 0);
|
params.base_level, params.base_layer);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
}
|
}
|
||||||
return;
|
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 GLenum view_target = surface.GetTarget();
|
||||||
const GLuint texture = surface.GetTexture();
|
const GLuint texture = surface.GetTexture();
|
||||||
switch (surface.GetSurfaceParams().target) {
|
switch (surface.GetSurfaceParams().target) {
|
||||||
case SurfaceTarget::Texture1D:
|
case SurfaceTarget::Texture1D:
|
||||||
glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level);
|
glFramebufferTexture1D(fb_target, attachment, view_target, texture, params.base_level);
|
||||||
break;
|
break;
|
||||||
case SurfaceTarget::Texture2D:
|
case SurfaceTarget::Texture2D:
|
||||||
glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level);
|
glFramebufferTexture2D(fb_target, attachment, view_target, texture, params.base_level);
|
||||||
break;
|
break;
|
||||||
case SurfaceTarget::Texture1DArray:
|
case SurfaceTarget::Texture1DArray:
|
||||||
case SurfaceTarget::Texture2DArray:
|
case SurfaceTarget::Texture2DArray:
|
||||||
case SurfaceTarget::TextureCubemap:
|
case SurfaceTarget::TextureCubemap:
|
||||||
case SurfaceTarget::TextureCubeArray:
|
case SurfaceTarget::TextureCubeArray:
|
||||||
glFramebufferTextureLayer(target, attachment, texture, params.base_level,
|
glFramebufferTextureLayer(fb_target, attachment, texture, params.base_level,
|
||||||
params.base_layer);
|
params.base_layer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -500,8 +508,13 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
||||||
OGLTextureView texture_view;
|
OGLTextureView texture_view;
|
||||||
texture_view.Create();
|
texture_view.Create();
|
||||||
|
|
||||||
glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level,
|
if (target == GL_TEXTURE_3D) {
|
||||||
params.num_levels, params.base_layer, params.num_layers);
|
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);
|
ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle);
|
||||||
|
|
||||||
return texture_view;
|
return texture_view;
|
||||||
|
|
|
@ -80,8 +80,10 @@ public:
|
||||||
explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy);
|
explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy);
|
||||||
~CachedSurfaceView();
|
~CachedSurfaceView();
|
||||||
|
|
||||||
/// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
|
/// @brief Attaches this texture view to the currently bound fb_target framebuffer
|
||||||
void Attach(GLenum attachment, GLenum target) const;
|
/// @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,
|
GLuint GetTexture(Tegra::Texture::SwizzleSource x_source,
|
||||||
Tegra::Texture::SwizzleSource y_source,
|
Tegra::Texture::SwizzleSource y_source,
|
||||||
|
|
|
@ -716,7 +716,7 @@ std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers(
|
||||||
if (!view) {
|
if (!view) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
key.views.push_back(view->GetHandle());
|
key.views.push_back(view->GetAttachment());
|
||||||
key.width = std::min(key.width, view->GetWidth());
|
key.width = std::min(key.width, view->GetWidth());
|
||||||
key.height = std::min(key.height, view->GetHeight());
|
key.height = std::min(key.height, view->GetHeight());
|
||||||
key.layers = std::min(key.layers, view->GetNumLayers());
|
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);
|
auto view = texture_cache.GetTextureSurface(texture.tic, entry);
|
||||||
ASSERT(!view->IsBufferView());
|
ASSERT(!view->IsBufferView());
|
||||||
|
|
||||||
const auto image_view = view->GetHandle(texture.tic.x_source, texture.tic.y_source,
|
const VkImageView image_view = view->GetImageView(texture.tic.x_source, texture.tic.y_source,
|
||||||
texture.tic.z_source, texture.tic.w_source);
|
texture.tic.z_source, texture.tic.w_source);
|
||||||
const auto sampler = sampler_cache.GetSampler(texture.tsc);
|
const auto sampler = sampler_cache.GetSampler(texture.tsc);
|
||||||
update_descriptor_queue.AddSampledImage(sampler, image_view);
|
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());
|
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);
|
update_descriptor_queue.AddImage(image_view);
|
||||||
|
|
||||||
const auto image_layout = update_descriptor_queue.GetLastImageLayout();
|
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};
|
ci.extent = {params.width, params.height, 1};
|
||||||
break;
|
break;
|
||||||
case SurfaceTarget::Texture3D:
|
case SurfaceTarget::Texture3D:
|
||||||
|
ci.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||||
ci.extent = {params.width, params.height, params.depth};
|
ci.extent = {params.width, params.height, params.depth};
|
||||||
break;
|
break;
|
||||||
case SurfaceTarget::TextureBuffer:
|
case SurfaceTarget::TextureBuffer:
|
||||||
|
@ -176,6 +177,12 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP
|
||||||
return ci;
|
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
|
} // Anonymous namespace
|
||||||
|
|
||||||
CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
|
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.
|
// TODO(Rodrigo): Move this to a virtual function.
|
||||||
main_view = CreateViewInner(
|
u32 num_layers = 1;
|
||||||
ViewParams(params.target, 0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels),
|
if (params.is_layered || params.target == SurfaceTarget::Texture3D) {
|
||||||
true);
|
num_layers = params.depth;
|
||||||
|
}
|
||||||
|
main_view = CreateView(ViewParams(params.target, 0, num_layers, 0, params.num_levels));
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedSurface::~CachedSurface() = default;
|
CachedSurface::~CachedSurface() = default;
|
||||||
|
@ -253,12 +262,8 @@ void CachedSurface::DecorateSurfaceName() {
|
||||||
}
|
}
|
||||||
|
|
||||||
View CachedSurface::CreateView(const ViewParams& params) {
|
View CachedSurface::CreateView(const ViewParams& params) {
|
||||||
return CreateViewInner(params, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
View CachedSurface::CreateViewInner(const ViewParams& params, bool is_proxy) {
|
|
||||||
// TODO(Rodrigo): Add name decorations
|
// 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) {
|
void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {
|
||||||
|
@ -342,18 +347,27 @@ VkImageSubresourceRange CachedSurface::GetImageSubresourceRange() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
|
CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
|
||||||
const ViewParams& params, bool is_proxy)
|
const ViewParams& params)
|
||||||
: VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()},
|
: VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()},
|
||||||
image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()},
|
image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()},
|
||||||
aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface},
|
aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface},
|
||||||
base_layer{params.base_layer}, num_layers{params.num_layers}, base_level{params.base_level},
|
base_level{params.base_level}, num_levels{params.num_levels},
|
||||||
num_levels{params.num_levels}, image_view_type{image ? GetImageViewType(params.target)
|
image_view_type{image ? GetImageViewType(params.target) : VK_IMAGE_VIEW_TYPE_1D} {
|
||||||
: 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;
|
CachedSurfaceView::~CachedSurfaceView() = default;
|
||||||
|
|
||||||
VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source,
|
VkImageView CachedSurfaceView::GetImageView(SwizzleSource x_source, SwizzleSource y_source,
|
||||||
SwizzleSource z_source, SwizzleSource w_source) {
|
SwizzleSource z_source, SwizzleSource w_source) {
|
||||||
const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
|
const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
|
||||||
if (last_image_view && last_swizzle == new_swizzle) {
|
if (last_image_view && last_swizzle == new_swizzle) {
|
||||||
return last_image_view;
|
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;
|
VkImageViewCreateInfo ci;
|
||||||
ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
ci.pNext = nullptr;
|
ci.pNext = nullptr;
|
||||||
|
@ -417,6 +436,35 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
|
||||||
return last_image_view = *image_view;
|
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,
|
VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||||
const VKDevice& device, VKResourceManager& resource_manager,
|
const VKDevice& device, VKResourceManager& resource_manager,
|
||||||
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
||||||
|
|
|
@ -91,7 +91,6 @@ protected:
|
||||||
void DecorateSurfaceName();
|
void DecorateSurfaceName();
|
||||||
|
|
||||||
View CreateView(const ViewParams& params) override;
|
View CreateView(const ViewParams& params) override;
|
||||||
View CreateViewInner(const ViewParams& params, bool is_proxy);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UploadBuffer(const std::vector<u8>& staging_buffer);
|
void UploadBuffer(const std::vector<u8>& staging_buffer);
|
||||||
|
@ -120,23 +119,20 @@ private:
|
||||||
class CachedSurfaceView final : public VideoCommon::ViewBase {
|
class CachedSurfaceView final : public VideoCommon::ViewBase {
|
||||||
public:
|
public:
|
||||||
explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
|
explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
|
||||||
const ViewParams& params, bool is_proxy);
|
const ViewParams& params);
|
||||||
~CachedSurfaceView();
|
~CachedSurfaceView();
|
||||||
|
|
||||||
VkImageView GetHandle(Tegra::Texture::SwizzleSource x_source,
|
VkImageView GetImageView(Tegra::Texture::SwizzleSource x_source,
|
||||||
Tegra::Texture::SwizzleSource y_source,
|
Tegra::Texture::SwizzleSource y_source,
|
||||||
Tegra::Texture::SwizzleSource z_source,
|
Tegra::Texture::SwizzleSource z_source,
|
||||||
Tegra::Texture::SwizzleSource w_source);
|
Tegra::Texture::SwizzleSource w_source);
|
||||||
|
|
||||||
|
VkImageView GetAttachment();
|
||||||
|
|
||||||
bool IsSameSurface(const CachedSurfaceView& rhs) const {
|
bool IsSameSurface(const CachedSurfaceView& rhs) const {
|
||||||
return &surface == &rhs.surface;
|
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 {
|
u32 GetWidth() const {
|
||||||
return params.GetMipWidth(base_level);
|
return params.GetMipWidth(base_level);
|
||||||
}
|
}
|
||||||
|
@ -180,14 +176,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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
|
// Store a copy of these values to avoid double dereference when reading them
|
||||||
const SurfaceParams params;
|
const SurfaceParams params;
|
||||||
const VkImage image;
|
const VkImage image;
|
||||||
|
@ -196,15 +184,18 @@ private:
|
||||||
|
|
||||||
const VKDevice& device;
|
const VKDevice& device;
|
||||||
CachedSurface& surface;
|
CachedSurface& surface;
|
||||||
const u32 base_layer;
|
|
||||||
const u32 num_layers;
|
|
||||||
const u32 base_level;
|
const u32 base_level;
|
||||||
const u32 num_levels;
|
const u32 num_levels;
|
||||||
const VkImageViewType image_view_type;
|
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;
|
VkImageView last_image_view = nullptr;
|
||||||
u32 last_swizzle = 0;
|
u32 last_swizzle = 0;
|
||||||
|
|
||||||
|
vk::ImageView render_target;
|
||||||
std::unordered_map<u32, vk::ImageView> view_cache;
|
std::unordered_map<u32, vk::ImageView> view_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -248,12 +248,11 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||||
|
|
||||||
// Use an extra temporal buffer
|
// Use an extra temporal buffer
|
||||||
auto& tmp_buffer = staging_cache.GetBuffer(1);
|
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);
|
tmp_buffer.resize(guest_memory_size);
|
||||||
host_ptr = tmp_buffer.data();
|
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);
|
memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,8 +217,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsProtected() const {
|
bool IsProtected() const {
|
||||||
// Only 3D Slices are to be protected
|
// Only 3D slices are to be protected
|
||||||
return is_target && params.block_depth > 0;
|
return is_target && params.target == SurfaceTarget::Texture3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRenderTarget() const {
|
bool IsRenderTarget() const {
|
||||||
|
@ -250,6 +250,11 @@ public:
|
||||||
return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels));
|
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,
|
std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params,
|
||||||
const GPUVAddr view_addr,
|
const GPUVAddr view_addr,
|
||||||
const std::size_t candidate_size, const u32 mipmap,
|
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,
|
std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr,
|
||||||
const std::size_t candidate_size) {
|
const std::size_t candidate_size) {
|
||||||
if (params.target == SurfaceTarget::Texture3D ||
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto layer_mipmap{GetLayerMipmap(view_addr)};
|
const auto layer_mipmap{GetLayerMipmap(view_addr)};
|
||||||
|
|
|
@ -215,10 +215,19 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
|
||||||
params.num_levels = 1;
|
params.num_levels = 1;
|
||||||
params.emulated_levels = 1;
|
params.emulated_levels = 1;
|
||||||
|
|
||||||
const bool is_layered = config.layers > 1 && params.block_depth == 0;
|
if (config.memory_layout.is_3d != 0) {
|
||||||
params.is_layered = is_layered;
|
params.depth = config.layers.Value();
|
||||||
params.depth = is_layered ? config.layers.Value() : 1;
|
params.is_layered = false;
|
||||||
params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
|
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;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -508,12 +508,12 @@ private:
|
||||||
return RecycleStrategy::Flush;
|
return RecycleStrategy::Flush;
|
||||||
}
|
}
|
||||||
// 3D Textures decision
|
// 3D Textures decision
|
||||||
if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) {
|
if (params.target == SurfaceTarget::Texture3D) {
|
||||||
return RecycleStrategy::Flush;
|
return RecycleStrategy::Flush;
|
||||||
}
|
}
|
||||||
for (const auto& s : overlaps) {
|
for (const auto& s : overlaps) {
|
||||||
const auto& s_params = s->GetSurfaceParams();
|
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;
|
return RecycleStrategy::Flush;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -726,76 +726,60 @@ private:
|
||||||
* @param params The parameters on the new surface.
|
* @param params The parameters on the new surface.
|
||||||
* @param gpu_addr The starting address of 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 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,
|
std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(VectorSurface& overlaps,
|
||||||
const SurfaceParams& params,
|
const SurfaceParams& params,
|
||||||
const GPUVAddr gpu_addr,
|
GPUVAddr gpu_addr, VAddr cpu_addr) {
|
||||||
const VAddr cpu_addr,
|
if (params.num_levels > 1) {
|
||||||
bool preserve_contents) {
|
// We can't handle mipmaps in 3D textures yet, better fallback to LLE approach
|
||||||
if (params.target == SurfaceTarget::Texture3D) {
|
return std::nullopt;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Manage 3D textures
|
||||||
if (params.block_depth > 0) {
|
if (params.target == SurfaceTarget::Texture3D) {
|
||||||
auto surface =
|
auto surface = Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr);
|
||||||
Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents);
|
|
||||||
if (surface) {
|
if (surface) {
|
||||||
return *surface;
|
return *surface;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue