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
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue