Przeglądaj źródła

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.
ReinUsesLisp 6 lat temu
rodzic
commit
6a0220b2e1

+ 7 - 2
src/video_core/engines/maxwell_3d.h

@@ -520,7 +520,7 @@ public:
                 BitField<12, 1, InvMemoryLayout> type;
             } memory_layout;
             union {
-                BitField<0, 16, u32> array_mode;
+                BitField<0, 16, u32> layers;
                 BitField<16, 1, u32> volume;
             };
             u32 layer_stride;
@@ -778,8 +778,12 @@ public:
 
                 u32 zeta_width;
                 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;
 
@@ -1475,6 +1479,7 @@ ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
 ASSERT_REG_POSITION(rt_control, 0x487);
 ASSERT_REG_POSITION(zeta_width, 0x48a);
 ASSERT_REG_POSITION(zeta_height, 0x48b);
+ASSERT_REG_POSITION(zeta_layers, 0x48c);
 ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
 ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
 ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);

+ 20 - 8
src/video_core/renderer_opengl/gl_texture_cache.cpp

@@ -398,24 +398,36 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
 CachedSurfaceView::~CachedSurfaceView() = default;
 
 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 (params.target) {
+        case SurfaceTarget::Texture2DArray:
+            glFramebufferTexture(target, attachment, texture, params.base_level);
+            break;
+        default:
+            UNIMPLEMENTED();
+        }
+        return;
+    }
 
-    switch (owner_params.target) {
+    const GLenum view_target = surface.GetTarget();
+    switch (surface.GetSurfaceParams().target) {
     case SurfaceTarget::Texture1D:
-        glFramebufferTexture1D(target, attachment, surface.GetTarget(), surface.GetTexture(),
-                               params.base_level);
+        glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level);
         break;
     case SurfaceTarget::Texture2D:
-        glFramebufferTexture2D(target, attachment, surface.GetTarget(), surface.GetTexture(),
-                               params.base_level);
+        glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level);
         break;
     case SurfaceTarget::Texture1DArray:
     case SurfaceTarget::Texture2DArray:
     case SurfaceTarget::TextureCubemap:
     case SurfaceTarget::TextureCubeArray:
-        glFramebufferTextureLayer(target, attachment, surface.GetTexture(), params.base_level,
+        glFramebufferTextureLayer(target, attachment, texture, params.base_level,
                                   params.base_layer);
         break;
     default:

+ 9 - 8
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@@ -602,33 +602,34 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen
 std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers(
     vk::RenderPass renderpass) {
     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) {
-        if (view == nullptr) {
+    const auto try_push = [&](const View& view) {
+        if (!view) {
             return false;
         }
         key.views.push_back(view->GetHandle());
         key.width = std::min(key.width, view->GetWidth());
         key.height = std::min(key.height, view->GetHeight());
+        key.layers = std::min(key.layers, view->GetNumLayers());
         return true;
     };
 
     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);
         }
     }
-    if (MarkAsModifiedAndPush(zeta_attachment)) {
+    if (try_push(zeta_attachment)) {
         texture_cache.MarkDepthBufferInUse();
     }
 
     const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key);
     auto& framebuffer = fbentry->second;
     if (is_cache_miss) {
-        const vk::FramebufferCreateInfo framebuffer_ci({}, key.renderpass,
-                                                       static_cast<u32>(key.views.size()),
-                                                       key.views.data(), key.width, key.height, 1);
+        const vk::FramebufferCreateInfo framebuffer_ci(
+            {}, key.renderpass, static_cast<u32>(key.views.size()), key.views.data(), key.width,
+            key.height, key.layers);
         const auto dev = device.GetLogical();
         const auto& dld = device.GetDispatchLoader();
         framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld);

+ 8 - 2
src/video_core/renderer_vulkan/vk_rasterizer.h

@@ -55,6 +55,7 @@ struct FramebufferCacheKey {
     vk::RenderPass renderpass{};
     u32 width = 0;
     u32 height = 0;
+    u32 layers = 0;
     ImageViewsPack views;
 
     std::size_t Hash() const noexcept {
@@ -65,12 +66,17 @@ struct FramebufferCacheKey {
         }
         boost::hash_combine(hash, width);
         boost::hash_combine(hash, height);
+        boost::hash_combine(hash, layers);
         return hash;
     }
 
     bool operator==(const FramebufferCacheKey& rhs) const noexcept {
-        return std::tie(renderpass, views, width, height) ==
-               std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height);
+        return std::tie(renderpass, views, width, height, layers) ==
+               std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height, rhs.layers);
+    }
+
+    bool operator!=(const FramebufferCacheKey& rhs) const noexcept {
+        return !operator==(rhs);
     }
 };
 

+ 4 - 0
src/video_core/renderer_vulkan/vk_texture_cache.h

@@ -151,6 +151,10 @@ public:
         return params.GetMipHeight(base_level);
     }
 
+    u32 GetNumLayers() const {
+        return num_layers;
+    }
+
     bool IsBufferView() const {
         return buffer_view;
     }

+ 24 - 23
src/video_core/texture_cache/surface_params.cpp

@@ -84,19 +84,16 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
     if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
         switch (params.pixel_format) {
         case PixelFormat::R16U:
-        case PixelFormat::R16F: {
+        case PixelFormat::R16F:
             params.pixel_format = PixelFormat::Z16;
             break;
-        }
-        case PixelFormat::R32F: {
+        case PixelFormat::R32F:
             params.pixel_format = PixelFormat::Z32F;
             break;
-        }
-        default: {
+        default:
             UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}",
                               static_cast<u32>(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;
 }
 
-SurfaceParams SurfaceParams::CreateForDepthBuffer(
-    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) {
+SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
+    const auto& regs = system.GPU().Maxwell3D().regs;
+    regs.zeta_width, regs.zeta_height, regs.zeta.format, regs.zeta.memory_layout.type;
     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.block_width = std::min(block_width, 5U);
-    params.block_height = std::min(block_height, 5U);
-    params.block_depth = std::min(block_depth, 5U);
+    params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U);
+    params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U);
+    params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
     params.tile_width_spacing = 1;
-    params.pixel_format = PixelFormatFromDepthFormat(format);
+    params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
     params.type = GetFormatType(params.pixel_format);
-    params.width = zeta_width;
-    params.height = zeta_height;
-    params.target = SurfaceTarget::Texture2D;
-    params.depth = 1;
+    params.width = regs.zeta_width;
+    params.height = regs.zeta_height;
     params.pitch = 0;
     params.num_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;
 }
 
@@ -214,11 +213,13 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
         params.width = params.pitch / bpp;
     }
     params.height = config.height;
-    params.depth = 1;
-    params.target = SurfaceTarget::Texture2D;
     params.num_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;
 }
 

+ 1 - 4
src/video_core/texture_cache/surface_params.h

@@ -35,10 +35,7 @@ public:
                                         const VideoCommon::Shader::Image& entry);
 
     /// Creates SurfaceCachedParams for a depth buffer configuration.
-    static SurfaceParams CreateForDepthBuffer(
-        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);
+    static SurfaceParams CreateForDepthBuffer(Core::System& system);
 
     /// Creates SurfaceCachedParams from a framebuffer configuration.
     static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);

+ 1 - 4
src/video_core/texture_cache/texture_cache.h

@@ -160,10 +160,7 @@ public:
             SetEmptyDepthBuffer();
             return {};
         }
-        const auto depth_params{SurfaceParams::CreateForDepthBuffer(
-            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)};
+        const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)};
         auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
         if (depth_buffer.target)
             depth_buffer.target->MarkAsRenderTarget(false, NO_RT);