texture_cache: Implement layered framebuffer attachments
Layered framebuffer attachments is a feature that allows applications to write attach layered textures to a single attachment. What layer the fragments are written to is decided from the shader using gl_Layer.
This commit is contained in:
parent
f552d553ba
commit
6a0220b2e1
|
@ -520,7 +520,7 @@ public:
|
||||||
BitField<12, 1, InvMemoryLayout> type;
|
BitField<12, 1, InvMemoryLayout> type;
|
||||||
} memory_layout;
|
} memory_layout;
|
||||||
union {
|
union {
|
||||||
BitField<0, 16, u32> array_mode;
|
BitField<0, 16, u32> layers;
|
||||||
BitField<16, 1, u32> volume;
|
BitField<16, 1, u32> volume;
|
||||||
};
|
};
|
||||||
u32 layer_stride;
|
u32 layer_stride;
|
||||||
|
@ -778,8 +778,12 @@ public:
|
||||||
|
|
||||||
u32 zeta_width;
|
u32 zeta_width;
|
||||||
u32 zeta_height;
|
u32 zeta_height;
|
||||||
|
union {
|
||||||
|
BitField<0, 16, u32> zeta_layers;
|
||||||
|
BitField<16, 1, u32> zeta_volume;
|
||||||
|
};
|
||||||
|
|
||||||
INSERT_UNION_PADDING_WORDS(0x27);
|
INSERT_UNION_PADDING_WORDS(0x26);
|
||||||
|
|
||||||
u32 depth_test_enable;
|
u32 depth_test_enable;
|
||||||
|
|
||||||
|
@ -1475,6 +1479,7 @@ ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
||||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||||
ASSERT_REG_POSITION(zeta_width, 0x48a);
|
ASSERT_REG_POSITION(zeta_width, 0x48a);
|
||||||
ASSERT_REG_POSITION(zeta_height, 0x48b);
|
ASSERT_REG_POSITION(zeta_height, 0x48b);
|
||||||
|
ASSERT_REG_POSITION(zeta_layers, 0x48c);
|
||||||
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
|
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
|
||||||
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
|
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
|
||||||
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
|
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
|
||||||
|
|
|
@ -398,24 +398,36 @@ 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 target) const {
|
||||||
ASSERT(params.num_layers == 1 && params.num_levels == 1);
|
ASSERT(params.num_levels == 1);
|
||||||
|
|
||||||
const auto& owner_params = surface.GetSurfaceParams();
|
const GLuint texture = surface.GetTexture();
|
||||||
|
if (params.num_layers > 1) {
|
||||||
|
// Layered framebuffer attachments
|
||||||
|
UNIMPLEMENTED_IF(params.base_layer != 0);
|
||||||
|
|
||||||
switch (owner_params.target) {
|
switch (params.target) {
|
||||||
|
case SurfaceTarget::Texture2DArray:
|
||||||
|
glFramebufferTexture(target, attachment, texture, params.base_level);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLenum view_target = surface.GetTarget();
|
||||||
|
switch (surface.GetSurfaceParams().target) {
|
||||||
case SurfaceTarget::Texture1D:
|
case SurfaceTarget::Texture1D:
|
||||||
glFramebufferTexture1D(target, attachment, surface.GetTarget(), surface.GetTexture(),
|
glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level);
|
||||||
params.base_level);
|
|
||||||
break;
|
break;
|
||||||
case SurfaceTarget::Texture2D:
|
case SurfaceTarget::Texture2D:
|
||||||
glFramebufferTexture2D(target, attachment, surface.GetTarget(), surface.GetTexture(),
|
glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level);
|
||||||
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, surface.GetTexture(), params.base_level,
|
glFramebufferTextureLayer(target, attachment, texture, params.base_level,
|
||||||
params.base_layer);
|
params.base_layer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -602,33 +602,34 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen
|
||||||
std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers(
|
std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers(
|
||||||
vk::RenderPass renderpass) {
|
vk::RenderPass renderpass) {
|
||||||
FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(),
|
FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(),
|
||||||
std::numeric_limits<u32>::max()};
|
std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
|
||||||
|
|
||||||
const auto MarkAsModifiedAndPush = [&](const View& view) {
|
const auto try_push = [&](const View& view) {
|
||||||
if (view == nullptr) {
|
if (!view) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
key.views.push_back(view->GetHandle());
|
key.views.push_back(view->GetHandle());
|
||||||
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());
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (std::size_t index = 0; index < std::size(color_attachments); ++index) {
|
for (std::size_t index = 0; index < std::size(color_attachments); ++index) {
|
||||||
if (MarkAsModifiedAndPush(color_attachments[index])) {
|
if (try_push(color_attachments[index])) {
|
||||||
texture_cache.MarkColorBufferInUse(index);
|
texture_cache.MarkColorBufferInUse(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (MarkAsModifiedAndPush(zeta_attachment)) {
|
if (try_push(zeta_attachment)) {
|
||||||
texture_cache.MarkDepthBufferInUse();
|
texture_cache.MarkDepthBufferInUse();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key);
|
const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key);
|
||||||
auto& framebuffer = fbentry->second;
|
auto& framebuffer = fbentry->second;
|
||||||
if (is_cache_miss) {
|
if (is_cache_miss) {
|
||||||
const vk::FramebufferCreateInfo framebuffer_ci({}, key.renderpass,
|
const vk::FramebufferCreateInfo framebuffer_ci(
|
||||||
static_cast<u32>(key.views.size()),
|
{}, key.renderpass, static_cast<u32>(key.views.size()), key.views.data(), key.width,
|
||||||
key.views.data(), key.width, key.height, 1);
|
key.height, key.layers);
|
||||||
const auto dev = device.GetLogical();
|
const auto dev = device.GetLogical();
|
||||||
const auto& dld = device.GetDispatchLoader();
|
const auto& dld = device.GetDispatchLoader();
|
||||||
framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld);
|
framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld);
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct FramebufferCacheKey {
|
||||||
vk::RenderPass renderpass{};
|
vk::RenderPass renderpass{};
|
||||||
u32 width = 0;
|
u32 width = 0;
|
||||||
u32 height = 0;
|
u32 height = 0;
|
||||||
|
u32 layers = 0;
|
||||||
ImageViewsPack views;
|
ImageViewsPack views;
|
||||||
|
|
||||||
std::size_t Hash() const noexcept {
|
std::size_t Hash() const noexcept {
|
||||||
|
@ -65,12 +66,17 @@ struct FramebufferCacheKey {
|
||||||
}
|
}
|
||||||
boost::hash_combine(hash, width);
|
boost::hash_combine(hash, width);
|
||||||
boost::hash_combine(hash, height);
|
boost::hash_combine(hash, height);
|
||||||
|
boost::hash_combine(hash, layers);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const FramebufferCacheKey& rhs) const noexcept {
|
bool operator==(const FramebufferCacheKey& rhs) const noexcept {
|
||||||
return std::tie(renderpass, views, width, height) ==
|
return std::tie(renderpass, views, width, height, layers) ==
|
||||||
std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height);
|
std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height, rhs.layers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const FramebufferCacheKey& rhs) const noexcept {
|
||||||
|
return !operator==(rhs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -151,6 +151,10 @@ public:
|
||||||
return params.GetMipHeight(base_level);
|
return params.GetMipHeight(base_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetNumLayers() const {
|
||||||
|
return num_layers;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsBufferView() const {
|
bool IsBufferView() const {
|
||||||
return buffer_view;
|
return buffer_view;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,19 +84,16 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
|
||||||
if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
|
if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
|
||||||
switch (params.pixel_format) {
|
switch (params.pixel_format) {
|
||||||
case PixelFormat::R16U:
|
case PixelFormat::R16U:
|
||||||
case PixelFormat::R16F: {
|
case PixelFormat::R16F:
|
||||||
params.pixel_format = PixelFormat::Z16;
|
params.pixel_format = PixelFormat::Z16;
|
||||||
break;
|
break;
|
||||||
}
|
case PixelFormat::R32F:
|
||||||
case PixelFormat::R32F: {
|
|
||||||
params.pixel_format = PixelFormat::Z32F;
|
params.pixel_format = PixelFormat::Z32F;
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
default: {
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}",
|
UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}",
|
||||||
static_cast<u32>(params.pixel_format));
|
static_cast<u32>(params.pixel_format));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
params.type = GetFormatType(params.pixel_format);
|
params.type = GetFormatType(params.pixel_format);
|
||||||
}
|
}
|
||||||
params.type = GetFormatType(params.pixel_format);
|
params.type = GetFormatType(params.pixel_format);
|
||||||
|
@ -168,27 +165,29 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceParams SurfaceParams::CreateForDepthBuffer(
|
SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
|
||||||
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||||
u32 block_width, u32 block_height, u32 block_depth,
|
regs.zeta_width, regs.zeta_height, regs.zeta.format, regs.zeta.memory_layout.type;
|
||||||
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
|
|
||||||
SurfaceParams params;
|
SurfaceParams params;
|
||||||
params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
|
params.is_tiled = regs.zeta.memory_layout.type ==
|
||||||
|
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
|
||||||
params.srgb_conversion = false;
|
params.srgb_conversion = false;
|
||||||
params.block_width = std::min(block_width, 5U);
|
params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U);
|
||||||
params.block_height = std::min(block_height, 5U);
|
params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U);
|
||||||
params.block_depth = std::min(block_depth, 5U);
|
params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
|
||||||
params.tile_width_spacing = 1;
|
params.tile_width_spacing = 1;
|
||||||
params.pixel_format = PixelFormatFromDepthFormat(format);
|
params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
|
||||||
params.type = GetFormatType(params.pixel_format);
|
params.type = GetFormatType(params.pixel_format);
|
||||||
params.width = zeta_width;
|
params.width = regs.zeta_width;
|
||||||
params.height = zeta_height;
|
params.height = regs.zeta_height;
|
||||||
params.target = SurfaceTarget::Texture2D;
|
|
||||||
params.depth = 1;
|
|
||||||
params.pitch = 0;
|
params.pitch = 0;
|
||||||
params.num_levels = 1;
|
params.num_levels = 1;
|
||||||
params.emulated_levels = 1;
|
params.emulated_levels = 1;
|
||||||
params.is_layered = false;
|
|
||||||
|
const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0;
|
||||||
|
params.is_layered = is_layered;
|
||||||
|
params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
|
||||||
|
params.depth = is_layered ? regs.zeta_layers.Value() : 1U;
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,11 +213,13 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
|
||||||
params.width = params.pitch / bpp;
|
params.width = params.pitch / bpp;
|
||||||
}
|
}
|
||||||
params.height = config.height;
|
params.height = config.height;
|
||||||
params.depth = 1;
|
|
||||||
params.target = SurfaceTarget::Texture2D;
|
|
||||||
params.num_levels = 1;
|
params.num_levels = 1;
|
||||||
params.emulated_levels = 1;
|
params.emulated_levels = 1;
|
||||||
params.is_layered = false;
|
|
||||||
|
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;
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,7 @@ public:
|
||||||
const VideoCommon::Shader::Image& entry);
|
const VideoCommon::Shader::Image& entry);
|
||||||
|
|
||||||
/// Creates SurfaceCachedParams for a depth buffer configuration.
|
/// Creates SurfaceCachedParams for a depth buffer configuration.
|
||||||
static SurfaceParams CreateForDepthBuffer(
|
static SurfaceParams CreateForDepthBuffer(Core::System& system);
|
||||||
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
|
|
||||||
u32 block_width, u32 block_height, u32 block_depth,
|
|
||||||
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
|
|
||||||
|
|
||||||
/// Creates SurfaceCachedParams from a framebuffer configuration.
|
/// Creates SurfaceCachedParams from a framebuffer configuration.
|
||||||
static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
|
static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
|
||||||
|
|
|
@ -160,10 +160,7 @@ public:
|
||||||
SetEmptyDepthBuffer();
|
SetEmptyDepthBuffer();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
|
const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)};
|
||||||
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
|
|
||||||
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
|
||||||
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
|
||||||
auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
|
auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
|
||||||
if (depth_buffer.target)
|
if (depth_buffer.target)
|
||||||
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
|
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
|
||||||
|
|
Reference in New Issue