summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/engines/const_buffer_engine_interface.h67
-rw-r--r--src/video_core/engines/kepler_compute.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.h45
-rw-r--r--src/video_core/engines/shader_bytecode.h11
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/guest_driver.cpp7
-rw-r--r--src/video_core/guest_driver.h21
-rw-r--r--src/video_core/memory_manager.h2
-rw-r--r--src/video_core/morton.cpp2
-rw-r--r--src/video_core/rasterizer_interface.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp111
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h23
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp510
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h99
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp409
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h22
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp404
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h153
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp109
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h34
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp288
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp48
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h45
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp130
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp138
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.h13
-rw-r--r--src/video_core/shader/const_buffer_locker.cpp126
-rw-r--r--src/video_core/shader/const_buffer_locker.h103
-rw-r--r--src/video_core/shader/control_flow.cpp13
-rw-r--r--src/video_core/shader/control_flow.h3
-rw-r--r--src/video_core/shader/decode.cpp22
-rw-r--r--src/video_core/shader/decode/bfe.cpp69
-rw-r--r--src/video_core/shader/decode/texture.cpp5
-rw-r--r--src/video_core/shader/node_helper.cpp2
-rw-r--r--src/video_core/shader/registry.cpp161
-rw-r--r--src/video_core/shader/registry.h137
-rw-r--r--src/video_core/shader/shader_ir.cpp5
-rw-r--r--src/video_core/shader/shader_ir.h6
-rw-r--r--src/video_core/shader/track.cpp18
-rw-r--r--src/video_core/shader/transform_feedback.cpp115
-rw-r--r--src/video_core/shader/transform_feedback.h23
-rw-r--r--src/video_core/surface.cpp2
-rw-r--r--src/video_core/surface.h142
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp3
-rw-r--r--src/video_core/texture_cache/surface_params.cpp6
-rw-r--r--src/video_core/texture_cache/texture_cache.h41
-rw-r--r--src/video_core/textures/astc.cpp1056
54 files changed, 2496 insertions, 2320 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 14f3b4569..91df062d7 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -65,8 +65,6 @@ add_library(video_core STATIC
renderer_opengl/gl_shader_decompiler.h
renderer_opengl/gl_shader_disk_cache.cpp
renderer_opengl/gl_shader_disk_cache.h
- renderer_opengl/gl_shader_gen.cpp
- renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_manager.cpp
renderer_opengl/gl_shader_manager.h
renderer_opengl/gl_shader_util.cpp
@@ -118,8 +116,6 @@ add_library(video_core STATIC
shader/ast.h
shader/compiler_settings.cpp
shader/compiler_settings.h
- shader/const_buffer_locker.cpp
- shader/const_buffer_locker.h
shader/control_flow.cpp
shader/control_flow.h
shader/decode.cpp
@@ -128,9 +124,13 @@ add_library(video_core STATIC
shader/node_helper.cpp
shader/node_helper.h
shader/node.h
+ shader/registry.cpp
+ shader/registry.h
shader/shader_ir.cpp
shader/shader_ir.h
shader/track.cpp
+ shader/transform_feedback.cpp
+ shader/transform_feedback.h
surface.cpp
surface.h
texture_cache/format_lookup_table.cpp
diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h
index d56a47710..724ee0fd6 100644
--- a/src/video_core/engines/const_buffer_engine_interface.h
+++ b/src/video_core/engines/const_buffer_engine_interface.h
@@ -16,11 +16,12 @@ namespace Tegra::Engines {
struct SamplerDescriptor {
union {
- BitField<0, 20, Tegra::Shader::TextureType> texture_type;
- BitField<20, 1, u32> is_array;
- BitField<21, 1, u32> is_buffer;
- BitField<22, 1, u32> is_shadow;
- u32 raw{};
+ u32 raw = 0;
+ BitField<0, 2, Tegra::Shader::TextureType> texture_type;
+ BitField<2, 3, Tegra::Texture::ComponentType> component_type;
+ BitField<5, 1, u32> is_array;
+ BitField<6, 1, u32> is_buffer;
+ BitField<7, 1, u32> is_shadow;
};
bool operator==(const SamplerDescriptor& rhs) const noexcept {
@@ -31,68 +32,48 @@ struct SamplerDescriptor {
return !operator==(rhs);
}
- static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) {
+ static SamplerDescriptor FromTIC(const Tegra::Texture::TICEntry& tic) {
+ using Tegra::Shader::TextureType;
SamplerDescriptor result;
- switch (tic_texture_type) {
+
+ // This is going to be used to determine the shading language type.
+ // Because of that we don't care about all component types on color textures.
+ result.component_type.Assign(tic.r_type.Value());
+
+ switch (tic.texture_type.Value()) {
case Tegra::Texture::TextureType::Texture1D:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
- result.is_array.Assign(0);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
+ result.texture_type.Assign(TextureType::Texture1D);
return result;
case Tegra::Texture::TextureType::Texture2D:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
- result.is_array.Assign(0);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
+ result.texture_type.Assign(TextureType::Texture2D);
return result;
case Tegra::Texture::TextureType::Texture3D:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D);
- result.is_array.Assign(0);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
+ result.texture_type.Assign(TextureType::Texture3D);
return result;
case Tegra::Texture::TextureType::TextureCubemap:
- result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
- result.is_array.Assign(0);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
+ result.texture_type.Assign(TextureType::TextureCube);
return result;
case Tegra::Texture::TextureType::Texture1DArray:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
+ result.texture_type.Assign(TextureType::Texture1D);
result.is_array.Assign(1);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
return result;
case Tegra::Texture::TextureType::Texture2DArray:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
+ result.texture_type.Assign(TextureType::Texture2D);
result.is_array.Assign(1);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
return result;
case Tegra::Texture::TextureType::Texture1DBuffer:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
- result.is_array.Assign(0);
+ result.texture_type.Assign(TextureType::Texture1D);
result.is_buffer.Assign(1);
- result.is_shadow.Assign(0);
return result;
case Tegra::Texture::TextureType::Texture2DNoMipmap:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
- result.is_array.Assign(0);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
+ result.texture_type.Assign(TextureType::Texture2D);
return result;
case Tegra::Texture::TextureType::TextureCubeArray:
- result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
+ result.texture_type.Assign(TextureType::TextureCube);
result.is_array.Assign(1);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
return result;
default:
- result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
- result.is_array.Assign(0);
- result.is_buffer.Assign(0);
- result.is_shadow.Assign(0);
+ result.texture_type.Assign(TextureType::Texture2D);
return result;
}
}
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index ae52afa79..1ecd65925 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -89,7 +89,7 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
- SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
+ SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
return result;
}
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 89050361e..ce536e29b 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -638,7 +638,7 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
- SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
+ SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
return result;
}
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index ed7fc8fdd..8a9e9992e 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -67,6 +67,7 @@ public:
static constexpr std::size_t NumVaryings = 31;
static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
static constexpr std::size_t NumClipDistances = 8;
+ static constexpr std::size_t NumTransformFeedbackBuffers = 4;
static constexpr std::size_t MaxShaderProgram = 6;
static constexpr std::size_t MaxShaderStage = 5;
// Maximum number of const buffers per shader stage.
@@ -627,6 +628,29 @@ public:
float depth_range_far;
};
+ struct TransformFeedbackBinding {
+ u32 buffer_enable;
+ u32 address_high;
+ u32 address_low;
+ s32 buffer_size;
+ s32 buffer_offset;
+ INSERT_UNION_PADDING_WORDS(3);
+
+ GPUVAddr Address() const {
+ return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
+ address_low);
+ }
+ };
+ static_assert(sizeof(TransformFeedbackBinding) == 32);
+
+ struct TransformFeedbackLayout {
+ u32 stream;
+ u32 varying_count;
+ u32 stride;
+ INSERT_UNION_PADDING_WORDS(1);
+ };
+ static_assert(sizeof(TransformFeedbackLayout) == 16);
+
bool IsShaderConfigEnabled(std::size_t index) const {
// The VertexB is always enabled.
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -635,6 +659,10 @@ public:
return shader_config[index].enable != 0;
}
+ bool IsShaderConfigEnabled(Regs::ShaderProgram type) const {
+ return IsShaderConfigEnabled(static_cast<std::size_t>(type));
+ }
+
union {
struct {
INSERT_UNION_PADDING_WORDS(0x45);
@@ -683,7 +711,13 @@ public:
u32 rasterize_enable;
- INSERT_UNION_PADDING_WORDS(0xF1);
+ std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings;
+
+ INSERT_UNION_PADDING_WORDS(0xC0);
+
+ std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts;
+
+ INSERT_UNION_PADDING_WORDS(0x1);
u32 tfb_enabled;
@@ -1202,7 +1236,11 @@ public:
u32 tex_cb_index;
- INSERT_UNION_PADDING_WORDS(0x395);
+ INSERT_UNION_PADDING_WORDS(0x7D);
+
+ std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
+
+ INSERT_UNION_PADDING_WORDS(0x298);
struct {
/// Compressed address of a buffer that holds information about bound SSBOs.
@@ -1428,6 +1466,8 @@ ASSERT_REG_POSITION(tess_mode, 0xC8);
ASSERT_REG_POSITION(tess_level_outer, 0xC9);
ASSERT_REG_POSITION(tess_level_inner, 0xCD);
ASSERT_REG_POSITION(rasterize_enable, 0xDF);
+ASSERT_REG_POSITION(tfb_bindings, 0xE0);
+ASSERT_REG_POSITION(tfb_layouts, 0x1C0);
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport_transform, 0x280);
@@ -1526,6 +1566,7 @@ ASSERT_REG_POSITION(firmware, 0x8C0);
ASSERT_REG_POSITION(const_buffer, 0x8E0);
ASSERT_REG_POSITION(cb_bind[0], 0x904);
ASSERT_REG_POSITION(tex_cb_index, 0x982);
+ASSERT_REG_POSITION(tfb_varying_locs, 0xA00);
ASSERT_REG_POSITION(ssbo_info, 0xD18);
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index c9bc83cd7..eba42deb4 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -911,14 +911,9 @@ union Instruction {
} fadd32i;
union {
- BitField<20, 8, u64> shift_position;
- BitField<28, 8, u64> shift_length;
- BitField<48, 1, u64> negate_b;
- BitField<49, 1, u64> negate_a;
-
- u64 GetLeftShiftValue() const {
- return 32 - (shift_position + shift_length);
- }
+ BitField<40, 1, u64> brev;
+ BitField<47, 1, u64> rd_cc;
+ BitField<48, 1, u64> is_signed;
} bfe;
union {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ba8c9d665..64acb17df 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -39,6 +39,7 @@ enum class RenderTargetFormat : u32 {
RGBA32_FLOAT = 0xC0,
RGBA32_UINT = 0xC2,
RGBA16_UNORM = 0xC6,
+ RGBA16_SNORM = 0xC7,
RGBA16_UINT = 0xC9,
RGBA16_FLOAT = 0xCA,
RG32_FLOAT = 0xCB,
diff --git a/src/video_core/guest_driver.cpp b/src/video_core/guest_driver.cpp
index 6adef459e..f058f2744 100644
--- a/src/video_core/guest_driver.cpp
+++ b/src/video_core/guest_driver.cpp
@@ -4,13 +4,15 @@
#include <algorithm>
#include <limits>
+#include <vector>
+#include "common/common_types.h"
#include "video_core/guest_driver.h"
namespace VideoCore {
-void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) {
- if (texture_handler_size_deduced) {
+void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32> bound_offsets) {
+ if (texture_handler_size) {
return;
}
const std::size_t size = bound_offsets.size();
@@ -29,7 +31,6 @@ void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offse
if (min_val > 2) {
return;
}
- texture_handler_size_deduced = true;
texture_handler_size = min_texture_handler_size * min_val;
}
diff --git a/src/video_core/guest_driver.h b/src/video_core/guest_driver.h
index fc1917347..99450777e 100644
--- a/src/video_core/guest_driver.h
+++ b/src/video_core/guest_driver.h
@@ -4,6 +4,7 @@
#pragma once
+#include <optional>
#include <vector>
#include "common/common_types.h"
@@ -17,25 +18,29 @@ namespace VideoCore {
*/
class GuestDriverProfile {
public:
- void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets);
+ explicit GuestDriverProfile() = default;
+ explicit GuestDriverProfile(std::optional<u32> texture_handler_size)
+ : texture_handler_size{texture_handler_size} {}
+
+ void DeduceTextureHandlerSize(std::vector<u32> bound_offsets);
u32 GetTextureHandlerSize() const {
- return texture_handler_size;
+ return texture_handler_size.value_or(default_texture_handler_size);
}
- bool TextureHandlerSizeKnown() const {
- return texture_handler_size_deduced;
+ bool IsTextureHandlerSizeKnown() const {
+ return texture_handler_size.has_value();
}
private:
// Minimum size of texture handler any driver can use.
static constexpr u32 min_texture_handler_size = 4;
- // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily
- // use 4 bytes instead. Thus, certain drivers may squish the size.
+
+ // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily use 4 bytes instead.
+ // Thus, certain drivers may squish the size.
static constexpr u32 default_texture_handler_size = 8;
- u32 texture_handler_size = default_texture_handler_size;
- bool texture_handler_size_deduced = false;
+ std::optional<u32> texture_handler_size = default_texture_handler_size;
};
} // namespace VideoCore
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index aea010087..073bdb491 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -174,7 +174,7 @@ private:
/// End of address space, based on address space in bits.
static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
- Common::PageTable page_table{page_bits};
+ Common::BackingPageTable page_table{page_bits};
VMAMap vma_map;
VideoCore::RasterizerInterface& rasterizer;
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index f2c83266e..6d522c318 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -51,6 +51,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
MortonCopy<true, PixelFormat::R8UI>,
MortonCopy<true, PixelFormat::RGBA16F>,
MortonCopy<true, PixelFormat::RGBA16U>,
+ MortonCopy<true, PixelFormat::RGBA16S>,
MortonCopy<true, PixelFormat::RGBA16UI>,
MortonCopy<true, PixelFormat::R11FG11FB10F>,
MortonCopy<true, PixelFormat::RGBA32UI>,
@@ -131,6 +132,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
MortonCopy<false, PixelFormat::R8U>,
MortonCopy<false, PixelFormat::R8UI>,
MortonCopy<false, PixelFormat::RGBA16F>,
+ MortonCopy<false, PixelFormat::RGBA16S>,
MortonCopy<false, PixelFormat::RGBA16U>,
MortonCopy<false, PixelFormat::RGBA16UI>,
MortonCopy<false, PixelFormat::R11FG11FB10F>,
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 3e4514b94..1a68e3caa 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -25,7 +25,6 @@ constexpr std::size_t NumQueryTypes = 1;
enum class LoadCallbackStage {
Prepare,
- Decompile,
Build,
Complete,
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8e48a6482..1af4268a4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -28,7 +28,6 @@
#include "video_core/renderer_opengl/gl_query_cache.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
-#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/maxwell_to_gl.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
@@ -76,7 +75,7 @@ Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry
}
std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
- const GLShader::ConstBufferEntry& entry) {
+ const ConstBufferEntry& entry) {
if (!entry.IsIndirect()) {
return entry.GetSize();
}
@@ -272,9 +271,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
SetupDrawTextures(stage, shader);
SetupDrawImages(stage, shader);
- const ProgramVariant variant(primitive_mode);
- const auto program_handle = shader->GetHandle(variant);
-
+ const GLuint program_handle = shader->GetHandle();
switch (program) {
case Maxwell::ShaderProgram::VertexA:
case Maxwell::ShaderProgram::VertexB:
@@ -295,7 +292,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
// When a clip distance is enabled but not set in the shader it crops parts of the screen
// (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
// clip distances only when it's written by a shader stage.
- clip_distances |= shader->GetShaderEntries().clip_distances;
+ clip_distances |= shader->GetEntries().clip_distances;
// When VertexA is enabled, we have dual vertex shaders
if (program == Maxwell::ShaderProgram::VertexA) {
@@ -499,7 +496,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
SyncCullMode();
SyncPrimitiveRestart();
SyncScissorTest();
- SyncTransformFeedback();
SyncPointState();
SyncPolygonOffset();
SyncAlphaTest();
@@ -572,7 +568,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
glTextureBarrier();
}
- ++num_queued_commands;
+ BeginTransformFeedback(primitive_mode);
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
const GLsizei num_instances =
@@ -611,6 +607,10 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
num_instances, base_instance);
}
}
+
+ EndTransformFeedback();
+
+ ++num_queued_commands;
}
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
@@ -623,12 +623,7 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
auto kernel = shader_cache.GetComputeKernel(code_addr);
SetupComputeTextures(kernel);
SetupComputeImages(kernel);
-
- const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
- const ProgramVariant variant(launch_desc.block_dim_x, launch_desc.block_dim_y,
- launch_desc.block_dim_z, launch_desc.shared_alloc,
- launch_desc.local_pos_alloc);
- program_manager.BindComputeShader(kernel->GetHandle(variant));
+ program_manager.BindComputeShader(kernel->GetHandle());
const std::size_t buffer_size =
Tegra::Engines::KeplerCompute::NumConstBuffers *
@@ -646,6 +641,7 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
bind_ubo_pushbuffer.Bind();
bind_ssbo_pushbuffer.Bind();
+ const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
++num_queued_commands;
}
@@ -750,7 +746,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, const Shad
const auto& shader_stage = stages[stage_index];
u32 binding = device.GetBaseBindings(stage_index).uniform_buffer;
- for (const auto& entry : shader->GetShaderEntries().const_buffers) {
+ for (const auto& entry : shader->GetEntries().const_buffers) {
const auto& buffer = shader_stage.const_buffers[entry.GetIndex()];
SetupConstBuffer(binding++, buffer, entry);
}
@@ -761,7 +757,7 @@ void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) {
const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
u32 binding = 0;
- for (const auto& entry : kernel->GetShaderEntries().const_buffers) {
+ for (const auto& entry : kernel->GetEntries().const_buffers) {
const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
Tegra::Engines::ConstBufferInfo buffer;
@@ -773,7 +769,7 @@ void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) {
}
void RasterizerOpenGL::SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
- const GLShader::ConstBufferEntry& entry) {
+ const ConstBufferEntry& entry) {
if (!buffer.enabled) {
// Set values to zero to unbind buffers
bind_ubo_pushbuffer.Push(binding, buffer_cache.GetEmptyBuffer(sizeof(float)), 0,
@@ -797,7 +793,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, const Shad
const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
u32 binding = device.GetBaseBindings(stage_index).shader_storage_buffer;
- for (const auto& entry : shader->GetShaderEntries().global_memory_entries) {
+ for (const auto& entry : shader->GetEntries().global_memory_entries) {
const auto addr{cbufs.const_buffers[entry.GetCbufIndex()].address + entry.GetCbufOffset()};
const auto gpu_addr{memory_manager.Read<u64>(addr)};
const auto size{memory_manager.Read<u32>(addr + 8)};
@@ -811,7 +807,7 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) {
const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
u32 binding = 0;
- for (const auto& entry : kernel->GetShaderEntries().global_memory_entries) {
+ for (const auto& entry : kernel->GetEntries().global_memory_entries) {
const auto addr{cbufs[entry.GetCbufIndex()].Address() + entry.GetCbufOffset()};
const auto gpu_addr{memory_manager.Read<u64>(addr)};
const auto size{memory_manager.Read<u32>(addr + 8)};
@@ -819,7 +815,7 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) {
}
}
-void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry,
+void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry,
GPUVAddr gpu_addr, std::size_t size) {
const auto alignment{device.GetShaderStorageBufferAlignment()};
const auto [ssbo, buffer_offset] =
@@ -831,7 +827,7 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& maxwell3d = system.GPU().Maxwell3D();
u32 binding = device.GetBaseBindings(stage_index).sampler;
- for (const auto& entry : shader->GetShaderEntries().samplers) {
+ for (const auto& entry : shader->GetEntries().samplers) {
const auto shader_type = static_cast<ShaderType>(stage_index);
for (std::size_t i = 0; i < entry.Size(); ++i) {
const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
@@ -844,7 +840,7 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& compute = system.GPU().KeplerCompute();
u32 binding = 0;
- for (const auto& entry : kernel->GetShaderEntries().samplers) {
+ for (const auto& entry : kernel->GetEntries().samplers) {
for (std::size_t i = 0; i < entry.Size(); ++i) {
const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i);
SetupTexture(binding++, texture, entry);
@@ -853,7 +849,7 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
}
void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
- const GLShader::SamplerEntry& entry) {
+ const SamplerEntry& entry) {
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
if (!view) {
// Can occur when texture addr is null or its memory is unmapped/invalid
@@ -876,7 +872,7 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) {
const auto& maxwell3d = system.GPU().Maxwell3D();
u32 binding = device.GetBaseBindings(stage_index).image;
- for (const auto& entry : shader->GetShaderEntries().images) {
+ for (const auto& entry : shader->GetEntries().images) {
const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic;
SetupImage(binding++, tic, entry);
@@ -886,14 +882,14 @@ void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& sh
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
const auto& compute = system.GPU().KeplerCompute();
u32 binding = 0;
- for (const auto& entry : shader->GetShaderEntries().images) {
+ for (const auto& entry : shader->GetEntries().images) {
const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic;
SetupImage(binding++, tic, entry);
}
}
void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
- const GLShader::ImageEntry& entry) {
+ const ImageEntry& entry) {
const auto view = texture_cache.GetImageSurface(tic, entry);
if (!view) {
glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
@@ -1297,11 +1293,6 @@ void RasterizerOpenGL::SyncScissorTest() {
}
}
-void RasterizerOpenGL::SyncTransformFeedback() {
- const auto& regs = system.GPU().Maxwell3D().regs;
- UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented");
-}
-
void RasterizerOpenGL::SyncPointState() {
auto& gpu = system.GPU().Maxwell3D();
auto& flags = gpu.dirty.flags;
@@ -1377,4 +1368,62 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
}
+void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
+ const auto& regs = system.GPU().Maxwell3D().regs;
+ if (regs.tfb_enabled == 0) {
+ return;
+ }
+
+ UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
+ regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
+ regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
+
+ for (std::size_t index = 0; index < Maxwell::NumTransformFeedbackBuffers; ++index) {
+ const auto& binding = regs.tfb_bindings[index];
+ if (!binding.buffer_enable) {
+ if (enabled_transform_feedback_buffers[index]) {
+ glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(index), 0, 0,
+ 0);
+ }
+ enabled_transform_feedback_buffers[index] = false;
+ continue;
+ }
+ enabled_transform_feedback_buffers[index] = true;
+
+ auto& tfb_buffer = transform_feedback_buffers[index];
+ tfb_buffer.Create();
+
+ const GLuint handle = tfb_buffer.handle;
+ const std::size_t size = binding.buffer_size;
+ glNamedBufferData(handle, static_cast<GLsizeiptr>(size), nullptr, GL_STREAM_COPY);
+ glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(index), handle, 0,
+ static_cast<GLsizeiptr>(size));
+ }
+
+ glBeginTransformFeedback(GL_POINTS);
+}
+
+void RasterizerOpenGL::EndTransformFeedback() {
+ const auto& regs = system.GPU().Maxwell3D().regs;
+ if (regs.tfb_enabled == 0) {
+ return;
+ }
+
+ glEndTransformFeedback();
+
+ for (std::size_t index = 0; index < Maxwell::NumTransformFeedbackBuffers; ++index) {
+ const auto& binding = regs.tfb_bindings[index];
+ if (!binding.buffer_enable) {
+ continue;
+ }
+ UNIMPLEMENTED_IF(binding.buffer_offset != 0);
+
+ const GLuint handle = transform_feedback_buffers[index].handle;
+ const GPUVAddr gpu_addr = binding.Address();
+ const std::size_t size = binding.buffer_size;
+ const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true);
+ glCopyNamedBufferSubData(handle, *dest_buffer, 0, offset, static_cast<GLsizeiptr>(size));
+ }
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index e5681d6df..2d3be2437 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -98,7 +98,7 @@ private:
/// Configures a constant buffer.
void SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
- const GLShader::ConstBufferEntry& entry);
+ const ConstBufferEntry& entry);
/// Configures the current global memory entries to use for the draw command.
void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader);
@@ -107,7 +107,7 @@ private:
void SetupComputeGlobalMemory(const Shader& kernel);
/// Configures a constant buffer.
- void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
+ void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
std::size_t size);
/// Configures the current textures to use for the draw command.
@@ -118,7 +118,7 @@ private:
/// Configures a texture.
void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
- const GLShader::SamplerEntry& entry);
+ const SamplerEntry& entry);
/// Configures images in a graphics shader.
void SetupDrawImages(std::size_t stage_index, const Shader& shader);
@@ -127,8 +127,7 @@ private:
void SetupComputeImages(const Shader& shader);
/// Configures an image.
- void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
- const GLShader::ImageEntry& entry);
+ void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
/// Syncs the viewport and depth range to match the guest state
void SyncViewport();
@@ -169,9 +168,6 @@ private:
/// Syncs the scissor test state to match the guest state
void SyncScissorTest();
- /// Syncs the transform feedback state to match the guest state
- void SyncTransformFeedback();
-
/// Syncs the point state to match the guest state
void SyncPointState();
@@ -193,6 +189,12 @@ private:
/// Syncs the framebuffer sRGB state to match the guest state
void SyncFramebufferSRGB();
+ /// Begin a transform feedback
+ void BeginTransformFeedback(GLenum primitive_mode);
+
+ /// End a transform feedback
+ void EndTransformFeedback();
+
/// Check for extension that are not strictly required but are needed for correct emulation
void CheckExtensions();
@@ -230,6 +232,11 @@ private:
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
+ std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
+ transform_feedback_buffers;
+ std::bitset<Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
+ enabled_transform_feedback_buffers;
+
/// Number of commands queued to the OpenGL driver. Reseted on flush.
std::size_t num_queued_commands = 0;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 4cb89db8c..e3d31c3eb 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -2,12 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <atomic>
+#include <functional>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <unordered_set>
+
#include <boost/functional/hash.hpp>
+
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
@@ -24,13 +28,14 @@
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/utils.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace OpenGL {
using Tegra::Engines::ShaderType;
-using VideoCommon::Shader::ConstBufferLocker;
using VideoCommon::Shader::ProgramCode;
+using VideoCommon::Shader::Registry;
using VideoCommon::Shader::ShaderIR;
namespace {
@@ -56,7 +61,7 @@ constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
}
/// Calculates the size of a program stream
-std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) {
+std::size_t CalculateProgramSize(const ProgramCode& program) {
constexpr std::size_t start_offset = 10;
// This is the encoded version of BRA that jumps to itself. All Nvidia
// shaders end with one.
@@ -109,32 +114,9 @@ constexpr GLenum GetGLShaderType(ShaderType shader_type) {
}
}
-/// Describes primitive behavior on geometry shaders
-constexpr std::pair<const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) {
- switch (primitive_mode) {
- case GL_POINTS:
- return {"points", 1};
- case GL_LINES:
- case GL_LINE_STRIP:
- return {"lines", 2};
- case GL_LINES_ADJACENCY:
- case GL_LINE_STRIP_ADJACENCY:
- return {"lines_adjacency", 4};
- case GL_TRIANGLES:
- case GL_TRIANGLE_STRIP:
- case GL_TRIANGLE_FAN:
- return {"triangles", 3};
- case GL_TRIANGLES_ADJACENCY:
- case GL_TRIANGLE_STRIP_ADJACENCY:
- return {"triangles_adjacency", 6};
- default:
- return {"points", 1};
- }
-}
-
/// Hashes one (or two) program streams
u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& code,
- const ProgramCode& code_b) {
+ const ProgramCode& code_b = {}) {
u64 unique_identifier = boost::hash_value(code);
if (is_a) {
// VertexA programs include two programs
@@ -143,24 +125,6 @@ u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& co
return unique_identifier;
}
-/// Creates an unspecialized program from code streams
-std::string GenerateGLSL(const Device& device, ShaderType shader_type, const ShaderIR& ir,
- const std::optional<ShaderIR>& ir_b) {
- switch (shader_type) {
- case ShaderType::Vertex:
- return GLShader::GenerateVertexShader(device, ir, ir_b ? &*ir_b : nullptr);
- case ShaderType::Geometry:
- return GLShader::GenerateGeometryShader(device, ir);
- case ShaderType::Fragment:
- return GLShader::GenerateFragmentShader(device, ir);
- case ShaderType::Compute:
- return GLShader::GenerateComputeShader(device, ir);
- default:
- UNIMPLEMENTED_MSG("Unimplemented shader_type={}", static_cast<u32>(shader_type));
- return {};
- }
-}
-
constexpr const char* GetShaderTypeName(ShaderType shader_type) {
switch (shader_type) {
case ShaderType::Vertex:
@@ -196,102 +160,38 @@ constexpr ShaderType GetShaderType(Maxwell::ShaderProgram program_type) {
return {};
}
-std::string GetShaderId(u64 unique_identifier, ShaderType shader_type) {
+std::string MakeShaderID(u64 unique_identifier, ShaderType shader_type) {
return fmt::format("{}{:016X}", GetShaderTypeName(shader_type), unique_identifier);
}
-Tegra::Engines::ConstBufferEngineInterface& GetConstBufferEngineInterface(Core::System& system,
- ShaderType shader_type) {
- if (shader_type == ShaderType::Compute) {
- return system.GPU().KeplerCompute();
- } else {
- return system.GPU().Maxwell3D();
- }
-}
-
-std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType shader_type) {
- return std::make_unique<ConstBufferLocker>(shader_type,
- GetConstBufferEngineInterface(system, shader_type));
-}
-
-void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
- locker.SetBoundBuffer(usage.bound_buffer);
- for (const auto& key : usage.keys) {
- const auto [buffer, offset] = key.first;
- locker.InsertKey(buffer, offset, key.second);
+std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
+ const VideoCore::GuestDriverProfile guest_profile{entry.texture_handler_size};
+ const VideoCommon::Shader::SerializedRegistryInfo info{guest_profile, entry.bound_buffer,
+ entry.graphics_info, entry.compute_info};
+ const auto registry = std::make_shared<Registry>(entry.type, info);
+ for (const auto& [address, value] : entry.keys) {
+ const auto [buffer, offset] = address;
+ registry->InsertKey(buffer, offset, value);
}
- for (const auto& [offset, sampler] : usage.bound_samplers) {
- locker.InsertBoundSampler(offset, sampler);
+ for (const auto& [offset, sampler] : entry.bound_samplers) {
+ registry->InsertBoundSampler(offset, sampler);
}
- for (const auto& [key, sampler] : usage.bindless_samplers) {
+ for (const auto& [key, sampler] : entry.bindless_samplers) {
const auto [buffer, offset] = key;
- locker.InsertBindlessSampler(buffer, offset, sampler);
+ registry->InsertBindlessSampler(buffer, offset, sampler);
}
+ return registry;
}
-CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderType shader_type,
- const ProgramCode& code, const ProgramCode& code_b,
- ConstBufferLocker& locker, const ProgramVariant& variant,
- bool hint_retrievable = false) {
- LOG_INFO(Render_OpenGL, "called. {}", GetShaderId(unique_identifier, shader_type));
-
- const bool is_compute = shader_type == ShaderType::Compute;
- const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
- const ShaderIR ir(code, main_offset, COMPILER_SETTINGS, locker);
- std::optional<ShaderIR> ir_b;
- if (!code_b.empty()) {
- ir_b.emplace(code_b, main_offset, COMPILER_SETTINGS, locker);
- }
-
- std::string source = fmt::format(R"(// {}
-#version 430 core
-#extension GL_ARB_separate_shader_objects : enable
-)",
- GetShaderId(unique_identifier, shader_type));
- if (device.HasShaderBallot()) {
- source += "#extension GL_ARB_shader_ballot : require\n";
- }
- if (device.HasVertexViewportLayer()) {
- source += "#extension GL_ARB_shader_viewport_layer_array : require\n";
- }
- if (device.HasImageLoadFormatted()) {
- source += "#extension GL_EXT_shader_image_load_formatted : require\n";
- }
- if (device.HasWarpIntrinsics()) {
- source += "#extension GL_NV_gpu_shader5 : require\n"
- "#extension GL_NV_shader_thread_group : require\n"
- "#extension GL_NV_shader_thread_shuffle : require\n";
- }
- // This pragma stops Nvidia's driver from over optimizing math (probably using fp16 operations)
- // on places where we don't want to.
- // Thanks to Ryujinx for finding this workaround.
- source += "#pragma optionNV(fastmath off)\n";
-
- if (shader_type == ShaderType::Geometry) {
- const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(variant.primitive_mode);
- source += fmt::format("#define MAX_VERTEX_INPUT {}\n", max_vertices);
- source += fmt::format("layout ({}) in;\n", glsl_topology);
- }
- if (shader_type == ShaderType::Compute) {
- if (variant.local_memory_size > 0) {
- source += fmt::format("#define LOCAL_MEMORY_SIZE {}\n",
- Common::AlignUp(variant.local_memory_size, 4) / 4);
- }
- source +=
- fmt::format("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
- variant.block_x, variant.block_y, variant.block_z);
-
- if (variant.shared_memory_size > 0) {
- // shared_memory_size is described in number of words
- source += fmt::format("shared uint smem[{}];\n", variant.shared_memory_size);
- }
- }
-
- source += '\n';
- source += GenerateGLSL(device, shader_type, ir, ir_b);
+std::shared_ptr<OGLProgram> BuildShader(const Device& device, ShaderType shader_type,
+ u64 unique_identifier, const ShaderIR& ir,
+ const Registry& registry, bool hint_retrievable = false) {
+ const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
+ LOG_INFO(Render_OpenGL, "{}", shader_id);
+ const std::string glsl = DecompileShader(device, ir, registry, shader_type, shader_id);
OGLShader shader;
- shader.Create(source.c_str(), GetGLShaderType(shader_type));
+ shader.Create(glsl.c_str(), GetGLShaderType(shader_type));
auto program = std::make_shared<OGLProgram>();
program->Create(true, hint_retrievable, shader.handle);
@@ -299,7 +199,7 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderTyp
}
std::unordered_set<GLenum> GetSupportedFormats() {
- GLint num_formats{};
+ GLint num_formats;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
std::vector<GLint> formats(num_formats);
@@ -314,115 +214,82 @@ std::unordered_set<GLenum> GetSupportedFormats() {
} // Anonymous namespace
-CachedShader::CachedShader(const ShaderParameters& params, ShaderType shader_type,
- GLShader::ShaderEntries entries, ProgramCode code, ProgramCode code_b)
- : RasterizerCacheObject{params.host_ptr}, system{params.system},
- disk_cache{params.disk_cache}, device{params.device}, cpu_addr{params.cpu_addr},
- unique_identifier{params.unique_identifier}, shader_type{shader_type},
- entries{std::move(entries)}, code{std::move(code)}, code_b{std::move(code_b)} {
- if (!params.precompiled_variants) {
- return;
- }
- for (const auto& pair : *params.precompiled_variants) {
- auto locker = MakeLocker(system, shader_type);
- const auto& usage = pair->first;
- FillLocker(*locker, usage);
-
- std::unique_ptr<LockerVariant>* locker_variant = nullptr;
- const auto it =
- std::find_if(locker_variants.begin(), locker_variants.end(), [&](const auto& variant) {
- return variant->locker->HasEqualKeys(*locker);
- });
- if (it == locker_variants.end()) {
- locker_variant = &locker_variants.emplace_back();
- *locker_variant = std::make_unique<LockerVariant>();
- locker_variant->get()->locker = std::move(locker);
- } else {
- locker_variant = &*it;
- }
- locker_variant->get()->programs.emplace(usage.variant, pair->second);
- }
+CachedShader::CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes,
+ std::shared_ptr<VideoCommon::Shader::Registry> registry,
+ ShaderEntries entries, std::shared_ptr<OGLProgram> program)
+ : RasterizerCacheObject{host_ptr}, registry{std::move(registry)}, entries{std::move(entries)},
+ cpu_addr{cpu_addr}, size_in_bytes{size_in_bytes}, program{std::move(program)} {}
+
+CachedShader::~CachedShader() = default;
+
+GLuint CachedShader::GetHandle() const {
+ DEBUG_ASSERT(registry->IsConsistent());
+ return program->handle;
}
Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
Maxwell::ShaderProgram program_type, ProgramCode code,
ProgramCode code_b) {
const auto shader_type = GetShaderType(program_type);
- params.disk_cache.SaveRaw(
- ShaderDiskCacheRaw(params.unique_identifier, shader_type, code, code_b));
+ const std::size_t size_in_bytes = code.size() * sizeof(u64);
- ConstBufferLocker locker(shader_type, params.system.GPU().Maxwell3D());
- const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, locker);
+ auto registry = std::make_shared<Registry>(shader_type, params.system.GPU().Maxwell3D());
+ const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
// TODO(Rodrigo): Handle VertexA shaders
// std::optional<ShaderIR> ir_b;
// if (!code_b.empty()) {
// ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
// }
- return std::shared_ptr<CachedShader>(new CachedShader(
- params, shader_type, GLShader::GetEntries(ir), std::move(code), std::move(code_b)));
+ auto program = BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
+
+ ShaderDiskCacheEntry entry;
+ entry.type = shader_type;
+ entry.code = std::move(code);
+ entry.code_b = std::move(code_b);
+ entry.unique_identifier = params.unique_identifier;
+ entry.bound_buffer = registry->GetBoundBuffer();
+ entry.graphics_info = registry->GetGraphicsInfo();
+ entry.keys = registry->GetKeys();
+ entry.bound_samplers = registry->GetBoundSamplers();
+ entry.bindless_samplers = registry->GetBindlessSamplers();
+ params.disk_cache.SaveEntry(std::move(entry));
+
+ return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr,
+ size_in_bytes, std::move(registry),
+ MakeEntries(ir), std::move(program)));
}
Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) {
- params.disk_cache.SaveRaw(
- ShaderDiskCacheRaw(params.unique_identifier, ShaderType::Compute, code));
-
- ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute,
- params.system.GPU().KeplerCompute());
- const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, locker);
- return std::shared_ptr<CachedShader>(new CachedShader(
- params, ShaderType::Compute, GLShader::GetEntries(ir), std::move(code), {}));
+ const std::size_t size_in_bytes = code.size() * sizeof(u64);
+
+ auto& engine = params.system.GPU().KeplerCompute();
+ auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
+ const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
+ const u64 uid = params.unique_identifier;
+ auto program = BuildShader(params.device, ShaderType::Compute, uid, ir, *registry);
+
+ ShaderDiskCacheEntry entry;
+ entry.type = ShaderType::Compute;
+ entry.code = std::move(code);
+ entry.unique_identifier = uid;
+ entry.bound_buffer = registry->GetBoundBuffer();
+ entry.compute_info = registry->GetComputeInfo();
+ entry.keys = registry->GetKeys();
+ entry.bound_samplers = registry->GetBoundSamplers();
+ entry.bindless_samplers = registry->GetBindlessSamplers();
+ params.disk_cache.SaveEntry(std::move(entry));
+
+ return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr,
+ size_in_bytes, std::move(registry),
+ MakeEntries(ir), std::move(program)));
}
Shader CachedShader::CreateFromCache(const ShaderParameters& params,
- const UnspecializedShader& unspecialized) {
- return std::shared_ptr<CachedShader>(new CachedShader(params, unspecialized.type,
- unspecialized.entries, unspecialized.code,
- unspecialized.code_b));
-}
-
-GLuint CachedShader::GetHandle(const ProgramVariant& variant) {
- EnsureValidLockerVariant();
-
- const auto [entry, is_cache_miss] = curr_locker_variant->programs.try_emplace(variant);
- auto& program = entry->second;
- if (!is_cache_miss) {
- return program->handle;
- }
-
- program = BuildShader(device, unique_identifier, shader_type, code, code_b,
- *curr_locker_variant->locker, variant);
- disk_cache.SaveUsage(GetUsage(variant, *curr_locker_variant->locker));
-
- LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
- return program->handle;
-}
-
-bool CachedShader::EnsureValidLockerVariant() {
- const auto previous_variant = curr_locker_variant;
- if (curr_locker_variant && !curr_locker_variant->locker->IsConsistent()) {
- curr_locker_variant = nullptr;
- }
- if (!curr_locker_variant) {
- for (auto& variant : locker_variants) {
- if (variant->locker->IsConsistent()) {
- curr_locker_variant = variant.get();
- }
- }
- }
- if (!curr_locker_variant) {
- auto& new_variant = locker_variants.emplace_back();
- new_variant = std::make_unique<LockerVariant>();
- new_variant->locker = MakeLocker(system, shader_type);
- curr_locker_variant = new_variant.get();
- }
- return previous_variant == curr_locker_variant;
-}
-
-ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
- const ConstBufferLocker& locker) const {
- return ShaderDiskCacheUsage{unique_identifier, variant,
- locker.GetBoundBuffer(), locker.GetKeys(),
- locker.GetBoundSamplers(), locker.GetBindlessSamplers()};
+ const PrecompiledShader& precompiled_shader,
+ std::size_t size_in_bytes) {
+ return std::shared_ptr<CachedShader>(new CachedShader(
+ params.host_ptr, params.cpu_addr, size_in_bytes, precompiled_shader.registry,
+ precompiled_shader.entries, precompiled_shader.program));
}
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
@@ -432,16 +299,12 @@ ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System&
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
- const auto transferable = disk_cache.LoadTransferable();
+ const std::optional transferable = disk_cache.LoadTransferable();
if (!transferable) {
return;
}
- const auto [raws, shader_usages] = *transferable;
- if (!GenerateUnspecializedShaders(stop_loading, callback, raws) || stop_loading) {
- return;
- }
- const auto dumps = disk_cache.LoadPrecompiled();
+ const std::vector gl_cache = disk_cache.LoadPrecompiled();
const auto supported_formats = GetSupportedFormats();
// Track if precompiled cache was altered during loading to know if we have to
@@ -450,77 +313,82 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
// Inform the frontend about shader build initialization
if (callback) {
- callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size());
+ callback(VideoCore::LoadCallbackStage::Build, 0, transferable->size());
}
std::mutex mutex;
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
- std::atomic_bool compilation_failed = false;
+ std::atomic_bool gl_cache_failed = false;
- const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
- std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages,
- const ShaderDumpsMap& dumps) {
+ const auto find_precompiled = [&gl_cache](u64 id) {
+ return std::find_if(gl_cache.begin(), gl_cache.end(),
+ [id](const auto& entry) { return entry.unique_identifier == id; });
+ };
+
+ const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
+ std::size_t end) {
context->MakeCurrent();
SCOPE_EXIT({ return context->DoneCurrent(); });
for (std::size_t i = begin; i < end; ++i) {
- if (stop_loading || compilation_failed) {
+ if (stop_loading) {
return;
}
- const auto& usage{shader_usages[i]};
- const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)};
- const auto dump{dumps.find(usage)};
-
- CachedProgram shader;
- if (dump != dumps.end()) {
- // If the shader is dumped, attempt to load it with
- shader = GeneratePrecompiledProgram(dump->second, supported_formats);
- if (!shader) {
- compilation_failed = true;
- return;
+ const auto& entry = (*transferable)[i];
+ const u64 uid = entry.unique_identifier;
+ const auto it = find_precompiled(uid);
+ const auto precompiled_entry = it != gl_cache.end() ? &*it : nullptr;
+
+ const bool is_compute = entry.type == ShaderType::Compute;
+ const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
+ auto registry = MakeRegistry(entry);
+ const ShaderIR ir(entry.code, main_offset, COMPILER_SETTINGS, *registry);
+
+ std::shared_ptr<OGLProgram> program;
+ if (precompiled_entry) {
+ // If the shader is precompiled, attempt to load it with
+ program = GeneratePrecompiledProgram(entry, *precompiled_entry, supported_formats);
+ if (!program) {
+ gl_cache_failed = true;
}
}
- if (!shader) {
- auto locker{MakeLocker(system, unspecialized.type)};
- FillLocker(*locker, usage);
-
- shader = BuildShader(device, usage.unique_identifier, unspecialized.type,
- unspecialized.code, unspecialized.code_b, *locker,
- usage.variant, true);
+ if (!program) {
+ // Otherwise compile it from GLSL
+ program = BuildShader(device, entry.type, uid, ir, *registry, true);
}
+ PrecompiledShader shader;
+ shader.program = std::move(program);
+ shader.registry = std::move(registry);
+ shader.entries = MakeEntries(ir);
+
std::scoped_lock lock{mutex};
if (callback) {
callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
- shader_usages.size());
+ transferable->size());
}
-
- precompiled_programs.emplace(usage, std::move(shader));
-
- // TODO(Rodrigo): Is there a better way to do this?
- precompiled_variants[usage.unique_identifier].push_back(
- precompiled_programs.find(usage));
+ runtime_cache.emplace(entry.unique_identifier, std::move(shader));
}
};
const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)};
- const std::size_t bucket_size{shader_usages.size() / num_workers};
+ const std::size_t bucket_size{transferable->size() / num_workers};
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
std::vector<std::thread> threads(num_workers);
for (std::size_t i = 0; i < num_workers; ++i) {
const bool is_last_worker = i + 1 == num_workers;
const std::size_t start{bucket_size * i};
- const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size};
+ const std::size_t end{is_last_worker ? transferable->size() : start + bucket_size};
// On some platforms the shared context has to be created from the GUI thread
contexts[i] = emu_window.CreateSharedContext();
- threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps);
+ threads[i] = std::thread(worker, contexts[i].get(), start, end);
}
for (auto& thread : threads) {
thread.join();
}
- if (compilation_failed) {
+ if (gl_cache_failed) {
// Invalidate the precompiled cache if a shader dumped shader was rejected
disk_cache.InvalidatePrecompiled();
precompiled_cache_altered = true;
@@ -533,11 +401,12 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
// TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw
// before precompiling them
- for (std::size_t i = 0; i < shader_usages.size(); ++i) {
- const auto& usage{shader_usages[i]};
- if (dumps.find(usage) == dumps.end()) {
- const auto& program{precompiled_programs.at(usage)};
- disk_cache.SaveDump(usage, program->handle);
+ for (std::size_t i = 0; i < transferable->size(); ++i) {
+ const u64 id = (*transferable)[i].unique_identifier;
+ const auto it = find_precompiled(id);
+ if (it == gl_cache.end()) {
+ const GLuint program = runtime_cache.at(id).program->handle;
+ disk_cache.SavePrecompiled(id, program);
precompiled_cache_altered = true;
}
}
@@ -547,80 +416,29 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
}
}
-const PrecompiledVariants* ShaderCacheOpenGL::GetPrecompiledVariants(u64 unique_identifier) const {
- const auto it = precompiled_variants.find(unique_identifier);
- return it == precompiled_variants.end() ? nullptr : &it->second;
-}
-
-CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram(
- const ShaderDiskCacheDump& dump, const std::unordered_set<GLenum>& supported_formats) {
- if (supported_formats.find(dump.binary_format) == supported_formats.end()) {
- LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing");
+std::shared_ptr<OGLProgram> ShaderCacheOpenGL::GeneratePrecompiledProgram(
+ const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
+ const std::unordered_set<GLenum>& supported_formats) {
+ if (supported_formats.find(precompiled_entry.binary_format) == supported_formats.end()) {
+ LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format, removing");
return {};
}
- CachedProgram shader = std::make_shared<OGLProgram>();
- shader->handle = glCreateProgram();
- glProgramParameteri(shader->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
- glProgramBinary(shader->handle, dump.binary_format, dump.binary.data(),
- static_cast<GLsizei>(dump.binary.size()));
-
- GLint link_status{};
- glGetProgramiv(shader->handle, GL_LINK_STATUS, &link_status);
+ auto program = std::make_shared<OGLProgram>();
+ program->handle = glCreateProgram();
+ glProgramParameteri(program->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
+ glProgramBinary(program->handle, precompiled_entry.binary_format,
+ precompiled_entry.binary.data(),
+ static_cast<GLsizei>(precompiled_entry.binary.size()));
+
+ GLint link_status;
+ glGetProgramiv(program->handle, GL_LINK_STATUS, &link_status);
if (link_status == GL_FALSE) {
- LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing");
+ LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver, removing");
return {};
}
- return shader;
-}
-
-bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
- const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback,
- const std::vector<ShaderDiskCacheRaw>& raws) {
- if (callback) {
- callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size());
- }
-
- for (std::size_t i = 0; i < raws.size(); ++i) {
- if (stop_loading) {
- return false;
- }
- const auto& raw{raws[i]};
- const u64 unique_identifier{raw.GetUniqueIdentifier()};
- const u64 calculated_hash{
- GetUniqueIdentifier(raw.GetType(), raw.HasProgramA(), raw.GetCode(), raw.GetCodeB())};
- if (unique_identifier != calculated_hash) {
- LOG_ERROR(Render_OpenGL,
- "Invalid hash in entry={:016x} (obtained hash={:016x}) - "
- "removing shader cache",
- raw.GetUniqueIdentifier(), calculated_hash);
- disk_cache.InvalidateTransferable();
- return false;
- }
-
- const u32 main_offset =
- raw.GetType() == ShaderType::Compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
- ConstBufferLocker locker(raw.GetType());
- const ShaderIR ir(raw.GetCode(), main_offset, COMPILER_SETTINGS, locker);
- // TODO(Rodrigo): Handle VertexA shaders
- // std::optional<ShaderIR> ir_b;
- // if (raw.HasProgramA()) {
- // ir_b.emplace(raw.GetProgramCodeB(), main_offset);
- // }
-
- UnspecializedShader unspecialized;
- unspecialized.entries = GLShader::GetEntries(ir);
- unspecialized.type = raw.GetType();
- unspecialized.code = raw.GetCode();
- unspecialized.code_b = raw.GetCodeB();
- unspecialized_shaders.emplace(raw.GetUniqueIdentifier(), unspecialized);
-
- if (callback) {
- callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size());
- }
- }
- return true;
+ return program;
}
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
@@ -648,17 +466,17 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
const auto unique_identifier = GetUniqueIdentifier(
GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
- const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
const auto cpu_addr{*memory_manager.GpuToCpuAddress(address)};
- const ShaderParameters params{system, disk_cache, precompiled_variants, device,
+ const ShaderParameters params{system, disk_cache, device,
cpu_addr, host_ptr, unique_identifier};
- const auto found = unspecialized_shaders.find(unique_identifier);
- if (found == unspecialized_shaders.end()) {
+ const auto found = runtime_cache.find(unique_identifier);
+ if (found == runtime_cache.end()) {
shader = CachedShader::CreateStageFromMemory(params, program, std::move(code),
std::move(code_b));
} else {
- shader = CachedShader::CreateFromCache(params, found->second);
+ const std::size_t size_in_bytes = code.size() * sizeof(u64);
+ shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
}
Register(shader);
@@ -673,19 +491,19 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
return kernel;
}
- // No kernel found - create a new one
+ // No kernel found, create a new one
auto code{GetShaderCode(memory_manager, code_addr, host_ptr)};
- const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code, {})};
- const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
+ const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
const auto cpu_addr{*memory_manager.GpuToCpuAddress(code_addr)};
- const ShaderParameters params{system, disk_cache, precompiled_variants, device,
+ const ShaderParameters params{system, disk_cache, device,
cpu_addr, host_ptr, unique_identifier};
- const auto found = unspecialized_shaders.find(unique_identifier);
- if (found == unspecialized_shaders.end()) {
+ const auto found = runtime_cache.find(unique_identifier);
+ if (found == runtime_cache.end()) {
kernel = CachedShader::CreateKernelFromMemory(params, std::move(code));
} else {
- kernel = CachedShader::CreateFromCache(params, found->second);
+ const std::size_t size_in_bytes = code.size() * sizeof(u64);
+ kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
}
Register(kernel);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7b1470db3..4935019fc 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -22,7 +22,7 @@
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
-#include "video_core/shader/const_buffer_locker.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace Core {
@@ -41,22 +41,17 @@ class RasterizerOpenGL;
struct UnspecializedShader;
using Shader = std::shared_ptr<CachedShader>;
-using CachedProgram = std::shared_ptr<OGLProgram>;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
-using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>;
-using PrecompiledVariants = std::vector<PrecompiledPrograms::iterator>;
-
-struct UnspecializedShader {
- GLShader::ShaderEntries entries;
- Tegra::Engines::ShaderType type;
- ProgramCode code;
- ProgramCode code_b;
+
+struct PrecompiledShader {
+ std::shared_ptr<OGLProgram> program;
+ std::shared_ptr<VideoCommon::Shader::Registry> registry;
+ ShaderEntries entries;
};
struct ShaderParameters {
Core::System& system;
ShaderDiskCacheOpenGL& disk_cache;
- const PrecompiledVariants* precompiled_variants;
const Device& device;
VAddr cpu_addr;
u8* host_ptr;
@@ -65,61 +60,45 @@ struct ShaderParameters {
class CachedShader final : public RasterizerCacheObject {
public:
- static Shader CreateStageFromMemory(const ShaderParameters& params,
- Maxwell::ShaderProgram program_type,
- ProgramCode program_code, ProgramCode program_code_b);
- static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code);
+ ~CachedShader();
- static Shader CreateFromCache(const ShaderParameters& params,
- const UnspecializedShader& unspecialized);
+ /// Gets the GL program handle for the shader
+ GLuint GetHandle() const;
+ /// Returns the guest CPU address of the shader
VAddr GetCpuAddr() const override {
return cpu_addr;
}
+ /// Returns the size in bytes of the shader
std::size_t GetSizeInBytes() const override {
- return code.size() * sizeof(u64);
+ return size_in_bytes;
}
/// Gets the shader entries for the shader
- const GLShader::ShaderEntries& GetShaderEntries() const {
+ const ShaderEntries& GetEntries() const {
return entries;
}
- /// Gets the GL program handle for the shader
- GLuint GetHandle(const ProgramVariant& variant);
-
-private:
- struct LockerVariant {
- std::unique_ptr<VideoCommon::Shader::ConstBufferLocker> locker;
- std::unordered_map<ProgramVariant, CachedProgram> programs;
- };
-
- explicit CachedShader(const ShaderParameters& params, Tegra::Engines::ShaderType shader_type,
- GLShader::ShaderEntries entries, ProgramCode program_code,
- ProgramCode program_code_b);
-
- bool EnsureValidLockerVariant();
-
- ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant,
- const VideoCommon::Shader::ConstBufferLocker& locker) const;
-
- Core::System& system;
- ShaderDiskCacheOpenGL& disk_cache;
- const Device& device;
-
- VAddr cpu_addr{};
-
- u64 unique_identifier{};
- Tegra::Engines::ShaderType shader_type{};
-
- GLShader::ShaderEntries entries;
+ static Shader CreateStageFromMemory(const ShaderParameters& params,
+ Maxwell::ShaderProgram program_type,
+ ProgramCode program_code, ProgramCode program_code_b);
+ static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code);
- ProgramCode code;
- ProgramCode code_b;
+ static Shader CreateFromCache(const ShaderParameters& params,
+ const PrecompiledShader& precompiled_shader,
+ std::size_t size_in_bytes);
- LockerVariant* curr_locker_variant = nullptr;
- std::vector<std::unique_ptr<LockerVariant>> locker_variants;
+private:
+ explicit CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes,
+ std::shared_ptr<VideoCommon::Shader::Registry> registry,
+ ShaderEntries entries, std::shared_ptr<OGLProgram> program);
+
+ std::shared_ptr<VideoCommon::Shader::Registry> registry;
+ ShaderEntries entries;
+ VAddr cpu_addr = 0;
+ std::size_t size_in_bytes = 0;
+ std::shared_ptr<OGLProgram> program;
};
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
@@ -142,25 +121,15 @@ protected:
void FlushObjectInner(const Shader& object) override {}
private:
- bool GenerateUnspecializedShaders(const std::atomic_bool& stop_loading,
- const VideoCore::DiskResourceLoadCallback& callback,
- const std::vector<ShaderDiskCacheRaw>& raws);
-
- CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
- const std::unordered_set<GLenum>& supported_formats);
-
- const PrecompiledVariants* GetPrecompiledVariants(u64 unique_identifier) const;
+ std::shared_ptr<OGLProgram> GeneratePrecompiledProgram(
+ const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
+ const std::unordered_set<GLenum>& supported_formats);
Core::System& system;
Core::Frontend::EmuWindow& emu_window;
const Device& device;
-
ShaderDiskCacheOpenGL disk_cache;
-
- PrecompiledPrograms precompiled_programs;
- std::unordered_map<u64, PrecompiledVariants> precompiled_variants;
-
- std::unordered_map<u64, UnspecializedShader> unspecialized_shaders;
+ std::unordered_map<u64, PrecompiledShader> runtime_cache;
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 3a41ed30c..849839fe3 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -23,8 +23,9 @@
#include "video_core/shader/ast.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
+#include "video_core/shader/transform_feedback.h"
-namespace OpenGL::GLShader {
+namespace OpenGL {
namespace {
@@ -36,6 +37,8 @@ using Tegra::Shader::IpaInterpMode;
using Tegra::Shader::IpaMode;
using Tegra::Shader::IpaSampleMode;
using Tegra::Shader::Register;
+using VideoCommon::Shader::BuildTransformFeedback;
+using VideoCommon::Shader::Registry;
using namespace std::string_literals;
using namespace VideoCommon::Shader;
@@ -48,6 +51,11 @@ class ExprDecompiler;
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
+constexpr std::array FLOAT_TYPES{"float", "vec2", "vec3", "vec4"};
+
+constexpr std::string_view INPUT_ATTRIBUTE_NAME = "in_attr";
+constexpr std::string_view OUTPUT_ATTRIBUTE_NAME = "out_attr";
+
struct TextureOffset {};
struct TextureDerivates {};
using TextureArgument = std::pair<Type, Node>;
@@ -56,6 +64,25 @@ using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
+constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt
+#define ftou floatBitsToUint
+#define itof intBitsToFloat
+#define utof uintBitsToFloat
+
+bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{
+ bvec2 is_nan1 = isnan(pair1);
+ bvec2 is_nan2 = isnan(pair2);
+ return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || is_nan2.y);
+}}
+
+const float fswzadd_modifiers_a[] = float[4](-1.0f, 1.0f, -1.0f, 0.0f );
+const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
+
+layout (std140, binding = {}) uniform vs_config {{
+ float y_direction;
+}};
+)";
+
class ShaderWriter final {
public:
void AddExpression(std::string_view text) {
@@ -269,12 +296,41 @@ const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
}
}
+/// Describes primitive behavior on geometry shaders
+std::pair<const char*, u32> GetPrimitiveDescription(Maxwell::PrimitiveTopology topology) {
+ switch (topology) {
+ case Maxwell::PrimitiveTopology::Points:
+ return {"points", 1};
+ case Maxwell::PrimitiveTopology::Lines:
+ case Maxwell::PrimitiveTopology::LineStrip:
+ return {"lines", 2};
+ case Maxwell::PrimitiveTopology::LinesAdjacency:
+ case Maxwell::PrimitiveTopology::LineStripAdjacency:
+ return {"lines_adjacency", 4};
+ case Maxwell::PrimitiveTopology::Triangles:
+ case Maxwell::PrimitiveTopology::TriangleStrip:
+ case Maxwell::PrimitiveTopology::TriangleFan:
+ return {"triangles", 3};
+ case Maxwell::PrimitiveTopology::TrianglesAdjacency:
+ case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
+ return {"triangles_adjacency", 6};
+ default:
+ UNIMPLEMENTED_MSG("topology={}", static_cast<int>(topology));
+ return {"points", 1};
+ }
+}
+
/// Generates code to use for a swizzle operation.
-constexpr const char* GetSwizzle(u32 element) {
+constexpr const char* GetSwizzle(std::size_t element) {
constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
return swizzle.at(element);
}
+constexpr const char* GetColorSwizzle(std::size_t element) {
+ constexpr std::array swizzle = {".r", ".g", ".b", ".a"};
+ return swizzle.at(element);
+}
+
/// Translate topology
std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
switch (topology) {
@@ -341,11 +397,66 @@ std::string FlowStackTopName(MetaStackClass stack) {
return stage == ShaderType::Vertex;
}
+struct GenericVaryingDescription {
+ std::string name;
+ u8 first_element = 0;
+ bool is_scalar = false;
+};
+
class GLSLDecompiler final {
public:
- explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderType stage,
- std::string suffix)
- : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
+ explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry,
+ ShaderType stage, std::string_view identifier, std::string_view suffix)
+ : device{device}, ir{ir}, registry{registry}, stage{stage},
+ identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} {
+ if (stage != ShaderType::Compute) {
+ transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo());
+ }
+ }
+
+ void Decompile() {
+ DeclareHeader();
+ DeclareVertex();
+ DeclareGeometry();
+ DeclareFragment();
+ DeclareCompute();
+ DeclareInputAttributes();
+ DeclareOutputAttributes();
+ DeclareImages();
+ DeclareSamplers();
+ DeclareGlobalMemory();
+ DeclareConstantBuffers();
+ DeclareLocalMemory();
+ DeclareRegisters();
+ DeclarePredicates();
+ DeclareInternalFlags();
+ DeclareCustomVariables();
+ DeclarePhysicalAttributeReader();
+
+ code.AddLine("void main() {{");
+ ++code.scope;
+
+ if (stage == ShaderType::Vertex) {
+ code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
+ }
+
+ if (ir.IsDecompiled()) {
+ DecompileAST();
+ } else {
+ DecompileBranchMode();
+ }
+
+ --code.scope;
+ code.AddLine("}}");
+ }
+
+ std::string GetResult() {
+ return code.GetResult();
+ }
+
+private:
+ friend class ASTDecompiler;
+ friend class ExprDecompiler;
void DecompileBranchMode() {
// VM's program counter
@@ -387,43 +498,36 @@ public:
void DecompileAST();
- void Decompile() {
- DeclareVertex();
- DeclareGeometry();
- DeclareRegisters();
- DeclareCustomVariables();
- DeclarePredicates();
- DeclareLocalMemory();
- DeclareInternalFlags();
- DeclareInputAttributes();
- DeclareOutputAttributes();
- DeclareConstantBuffers();
- DeclareGlobalMemory();
- DeclareSamplers();
- DeclareImages();
- DeclarePhysicalAttributeReader();
-
- code.AddLine("void execute_{}() {{", suffix);
- ++code.scope;
-
- if (ir.IsDecompiled()) {
- DecompileAST();
- } else {
- DecompileBranchMode();
+ void DeclareHeader() {
+ if (!identifier.empty()) {
+ code.AddLine("// {}", identifier);
+ }
+ code.AddLine("#version 440 core");
+ code.AddLine("#extension GL_ARB_separate_shader_objects : enable");
+ if (device.HasShaderBallot()) {
+ code.AddLine("#extension GL_ARB_shader_ballot : require");
+ }
+ if (device.HasVertexViewportLayer()) {
+ code.AddLine("#extension GL_ARB_shader_viewport_layer_array : require");
+ }
+ if (device.HasImageLoadFormatted()) {
+ code.AddLine("#extension GL_EXT_shader_image_load_formatted : require");
+ }
+ if (device.HasWarpIntrinsics()) {
+ code.AddLine("#extension GL_NV_gpu_shader5 : require");
+ code.AddLine("#extension GL_NV_shader_thread_group : require");
+ code.AddLine("#extension GL_NV_shader_thread_shuffle : require");
}
+ // This pragma stops Nvidia's driver from over optimizing math (probably using fp16
+ // operations) on places where we don't want to.
+ // Thanks to Ryujinx for finding this workaround.
+ code.AddLine("#pragma optionNV(fastmath off)");
- --code.scope;
- code.AddLine("}}");
- }
+ code.AddNewLine();
- std::string GetResult() {
- return code.GetResult();
+ code.AddLine(CommonDeclarations, EmulationUniformBlockBinding);
}
-private:
- friend class ASTDecompiler;
- friend class ExprDecompiler;
-
void DeclareVertex() {
if (!IsVertexShader(stage))
return;
@@ -436,9 +540,15 @@ private:
return;
}
+ const auto& info = registry.GetGraphicsInfo();
+ const auto input_topology = info.primitive_topology;
+ const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(input_topology);
+ max_input_vertices = max_vertices;
+ code.AddLine("layout ({}) in;", glsl_topology);
+
const auto topology = GetTopologyName(header.common3.output_topology);
- const auto max_vertices = header.common4.max_output_vertices.Value();
- code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
+ const auto max_output_vertices = header.common4.max_output_vertices.Value();
+ code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_output_vertices);
code.AddNewLine();
code.AddLine("in gl_PerVertex {{");
@@ -450,11 +560,40 @@ private:
DeclareVertexRedeclarations();
}
+ void DeclareFragment() {
+ if (stage != ShaderType::Fragment) {
+ return;
+ }
+ for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
+ code.AddLine("layout (location = {}) out vec4 frag_color{};", rt, rt);
+ }
+ }
+
+ void DeclareCompute() {
+ if (stage != ShaderType::Compute) {
+ return;
+ }
+ const auto& info = registry.GetComputeInfo();
+ if (const u32 size = info.shared_memory_size_in_words; size > 0) {
+ code.AddLine("shared uint smem[{}];", size);
+ code.AddNewLine();
+ }
+ code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
+ info.workgroup_size[0], info.workgroup_size[1], info.workgroup_size[2]);
+ code.AddNewLine();
+ }
+
void DeclareVertexRedeclarations() {
code.AddLine("out gl_PerVertex {{");
++code.scope;
- code.AddLine("vec4 gl_Position;");
+ auto pos_xfb = GetTransformFeedbackDecoration(Attribute::Index::Position);
+ if (!pos_xfb.empty()) {
+ pos_xfb = fmt::format("layout ({}) ", pos_xfb);
+ }
+ const char* pos_type =
+ FLOAT_TYPES.at(GetNumComponents(Attribute::Index::Position).value_or(4) - 1);
+ code.AddLine("{}{} gl_Position;", pos_xfb, pos_type);
for (const auto attribute : ir.GetOutputAttributes()) {
if (attribute == Attribute::Index::ClipDistances0123 ||
@@ -525,18 +664,16 @@ private:
}
void DeclareLocalMemory() {
+ u64 local_memory_size = 0;
if (stage == ShaderType::Compute) {
- code.AddLine("#ifdef LOCAL_MEMORY_SIZE");
- code.AddLine("uint {}[LOCAL_MEMORY_SIZE];", GetLocalMemory());
- code.AddLine("#endif");
- return;
+ local_memory_size = registry.GetComputeInfo().local_memory_size_in_words * 4ULL;
+ } else {
+ local_memory_size = header.GetLocalMemorySize();
}
-
- const u64 local_memory_size = header.GetLocalMemorySize();
if (local_memory_size == 0) {
return;
}
- const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
+ const u64 element_count = Common::AlignUp(local_memory_size, 4) / 4;
code.AddLine("uint {}[{}];", GetLocalMemory(), element_count);
code.AddNewLine();
}
@@ -589,7 +726,7 @@ private:
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
const u32 location{GetGenericAttributeIndex(index)};
- std::string name{GetInputAttribute(index)};
+ std::string name{GetGenericInputAttribute(index)};
if (stage == ShaderType::Geometry) {
name = "gs_" + name + "[]";
}
@@ -626,9 +763,59 @@ private:
}
}
+ std::optional<std::size_t> GetNumComponents(Attribute::Index index, u8 element = 0) const {
+ const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
+ const auto it = transform_feedback.find(location);
+ if (it == transform_feedback.end()) {
+ return {};
+ }
+ return it->second.components;
+ }
+
+ std::string GetTransformFeedbackDecoration(Attribute::Index index, u8 element = 0) const {
+ const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
+ const auto it = transform_feedback.find(location);
+ if (it == transform_feedback.end()) {
+ return {};
+ }
+
+ const VaryingTFB& tfb = it->second;
+ return fmt::format("xfb_buffer = {}, xfb_offset = {}, xfb_stride = {}", tfb.buffer,
+ tfb.offset, tfb.stride);
+ }
+
void DeclareOutputAttribute(Attribute::Index index) {
- const u32 location{GetGenericAttributeIndex(index)};
- code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
+ static constexpr std::string_view swizzle = "xyzw";
+ u8 element = 0;
+ while (element < 4) {
+ auto xfb = GetTransformFeedbackDecoration(index, element);
+ if (!xfb.empty()) {
+ xfb = fmt::format(", {}", xfb);
+ }
+ const std::size_t remainder = 4 - element;
+ const std::size_t num_components = GetNumComponents(index, element).value_or(remainder);
+ const char* const type = FLOAT_TYPES.at(num_components - 1);
+
+ const u32 location = GetGenericAttributeIndex(index);
+
+ GenericVaryingDescription description;
+ description.first_element = static_cast<u8>(element);
+ description.is_scalar = num_components == 1;
+ description.name = AppendSuffix(location, OUTPUT_ATTRIBUTE_NAME);
+ if (element != 0 || num_components != 4) {
+ const std::string_view name_swizzle = swizzle.substr(element, num_components);
+ description.name = fmt::format("{}_{}", description.name, name_swizzle);
+ }
+ for (std::size_t i = 0; i < num_components; ++i) {
+ const u8 offset = static_cast<u8>(location * 4 + element + i);
+ varying_description.insert({offset, description});
+ }
+
+ code.AddLine("layout (location = {}, component = {}{}) out {} {};", location, element,
+ xfb, type, description.name);
+
+ element = static_cast<u8>(static_cast<std::size_t>(element) + num_components);
+ }
}
void DeclareConstantBuffers() {
@@ -925,7 +1112,8 @@ private:
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
// set an 0x80000000 index for those and the shader fails to build. Find out why
// this happens and what's its intent.
- return fmt::format("gs_{}[{} % MAX_VERTEX_INPUT]", name, Visit(buffer).AsUint());
+ return fmt::format("gs_{}[{} % {}]", name, Visit(buffer).AsUint(),
+ max_input_vertices.value());
}
return std::string(name);
};
@@ -980,7 +1168,7 @@ private:
return {"0", Type::Int};
default:
if (IsGenericAttribute(attribute)) {
- return {GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element),
+ return {GeometryPass(GetGenericInputAttribute(attribute)) + GetSwizzle(element),
Type::Float};
}
break;
@@ -1049,8 +1237,7 @@ private:
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}};
default:
if (IsGenericAttribute(attribute)) {
- return {
- {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), Type::Float}};
+ return {{GetGenericOutputAttribute(attribute, abuf->GetElement()), Type::Float}};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
return {};
@@ -1822,16 +2009,19 @@ private:
expr += GetSampler(meta->sampler);
expr += ", ";
- expr += constructors.at(operation.GetOperandsCount() - 1);
+ expr += constructors.at(operation.GetOperandsCount() + (meta->array ? 1 : 0) - 1);
expr += '(';
for (std::size_t i = 0; i < count; ++i) {
- expr += VisitOperand(operation, i).AsInt();
- const std::size_t next = i + 1;
- if (next == count)
- expr += ')';
- else if (next < count)
+ if (i > 0) {
expr += ", ";
+ }
+ expr += VisitOperand(operation, i).AsInt();
+ }
+ if (meta->array) {
+ expr += ", ";
+ expr += Visit(meta->array).AsInt();
}
+ expr += ')';
if (meta->lod && !meta->sampler.IsBuffer()) {
expr += ", ";
@@ -1945,7 +2135,7 @@ private:
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
for (u32 component = 0; component < 4; ++component) {
if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
- code.AddLine("FragColor{}[{}] = {};", render_target, component,
+ code.AddLine("frag_color{}{} = {};", render_target, GetColorSwizzle(component),
SafeGetRegister(current_reg).AsFloat());
++current_reg;
}
@@ -2261,27 +2451,34 @@ private:
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
std::string GetRegister(u32 index) const {
- return GetDeclarationWithSuffix(index, "gpr");
+ return AppendSuffix(index, "gpr");
}
std::string GetCustomVariable(u32 index) const {
- return GetDeclarationWithSuffix(index, "custom_var");
+ return AppendSuffix(index, "custom_var");
}
std::string GetPredicate(Tegra::Shader::Pred pred) const {
- return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
+ return AppendSuffix(static_cast<u32>(pred), "pred");
}
- std::string GetInputAttribute(Attribute::Index attribute) const {
- return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
+ std::string GetGenericInputAttribute(Attribute::Index attribute) const {
+ return AppendSuffix(GetGenericAttributeIndex(attribute), INPUT_ATTRIBUTE_NAME);
}
- std::string GetOutputAttribute(Attribute::Index attribute) const {
- return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
+ std::unordered_map<u8, GenericVaryingDescription> varying_description;
+
+ std::string GetGenericOutputAttribute(Attribute::Index attribute, std::size_t element) const {
+ const u8 offset = static_cast<u8>(GetGenericAttributeIndex(attribute) * 4 + element);
+ const auto& description = varying_description.at(offset);
+ if (description.is_scalar) {
+ return description.name;
+ }
+ return fmt::format("{}[{}]", description.name, element - description.first_element);
}
std::string GetConstBuffer(u32 index) const {
- return GetDeclarationWithSuffix(index, "cbuf");
+ return AppendSuffix(index, "cbuf");
}
std::string GetGlobalMemory(const GlobalMemoryBase& descriptor) const {
@@ -2294,11 +2491,15 @@ private:
}
std::string GetConstBufferBlock(u32 index) const {
- return GetDeclarationWithSuffix(index, "cbuf_block");
+ return AppendSuffix(index, "cbuf_block");
}
std::string GetLocalMemory() const {
- return "lmem_" + suffix;
+ if (suffix.empty()) {
+ return "lmem";
+ } else {
+ return "lmem_" + std::string{suffix};
+ }
}
std::string GetInternalFlag(InternalFlag flag) const {
@@ -2307,19 +2508,27 @@ private:
const auto index = static_cast<u32>(flag);
ASSERT(index < static_cast<u32>(InternalFlag::Amount));
- return fmt::format("{}_{}", InternalFlagNames[index], suffix);
+ if (suffix.empty()) {
+ return InternalFlagNames[index];
+ } else {
+ return fmt::format("{}_{}", InternalFlagNames[index], suffix);
+ }
}
std::string GetSampler(const Sampler& sampler) const {
- return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
+ return AppendSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
}
std::string GetImage(const Image& image) const {
- return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
+ return AppendSuffix(static_cast<u32>(image.GetIndex()), "image");
}
- std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
- return fmt::format("{}_{}_{}", name, index, suffix);
+ std::string AppendSuffix(u32 index, std::string_view name) const {
+ if (suffix.empty()) {
+ return fmt::format("{}{}", name, index);
+ } else {
+ return fmt::format("{}{}_{}", name, index, suffix);
+ }
}
u32 GetNumPhysicalInputAttributes() const {
@@ -2334,17 +2543,31 @@ private:
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
}
+ bool IsRenderTargetEnabled(u32 render_target) const {
+ for (u32 component = 0; component < 4; ++component) {
+ if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
const Device& device;
const ShaderIR& ir;
+ const Registry& registry;
const ShaderType stage;
- const std::string suffix;
+ const std::string_view identifier;
+ const std::string_view suffix;
const Header header;
+ std::unordered_map<u8, VaryingTFB> transform_feedback;
ShaderWriter code;
+
+ std::optional<u32> max_input_vertices;
};
-std::string GetFlowVariable(u32 i) {
- return fmt::format("flow_var_{}", i);
+std::string GetFlowVariable(u32 index) {
+ return fmt::format("flow_var{}", index);
}
class ExprDecompiler {
@@ -2531,7 +2754,7 @@ void GLSLDecompiler::DecompileAST() {
} // Anonymous namespace
-ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
+ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) {
ShaderEntries entries;
for (const auto& cbuf : ir.GetConstantBuffers()) {
entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
@@ -2555,28 +2778,12 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
return entries;
}
-std::string GetCommonDeclarations() {
- return R"(#define ftoi floatBitsToInt
-#define ftou floatBitsToUint
-#define itof intBitsToFloat
-#define utof uintBitsToFloat
-
-bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {
- bvec2 is_nan1 = isnan(pair1);
- bvec2 is_nan2 = isnan(pair2);
- return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || is_nan2.y);
-}
-
-const float fswzadd_modifiers_a[] = float[4](-1.0f, 1.0f, -1.0f, 0.0f );
-const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
-)";
-}
-
-std::string Decompile(const Device& device, const ShaderIR& ir, ShaderType stage,
- const std::string& suffix) {
- GLSLDecompiler decompiler(device, ir, stage, suffix);
+std::string DecompileShader(const Device& device, const ShaderIR& ir, const Registry& registry,
+ ShaderType stage, std::string_view identifier,
+ std::string_view suffix) {
+ GLSLDecompiler decompiler(device, ir, registry, stage, identifier, suffix);
decompiler.Decompile();
return decompiler.GetResult();
}
-} // namespace OpenGL::GLShader
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 0f692c1db..e7dbd810c 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -6,22 +6,18 @@
#include <array>
#include <string>
+#include <string_view>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_type.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
-namespace VideoCommon::Shader {
-class ShaderIR;
-}
-
namespace OpenGL {
-class Device;
-}
-namespace OpenGL::GLShader {
+class Device;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using SamplerEntry = VideoCommon::Shader::Sampler;
@@ -78,11 +74,11 @@ struct ShaderEntries {
std::size_t shader_length{};
};
-ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir);
-
-std::string GetCommonDeclarations();
+ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir);
-std::string Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
- Tegra::Engines::ShaderType stage, const std::string& suffix);
+std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
+ const VideoCommon::Shader::Registry& registry,
+ Tegra::Engines::ShaderType stage, std::string_view identifier,
+ std::string_view suffix = {});
-} // namespace OpenGL::GLShader
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 1fc204f6f..9e95a122b 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -31,32 +31,24 @@ namespace {
using ShaderCacheVersionHash = std::array<u8, 64>;
-enum class TransferableEntryKind : u32 {
- Raw,
- Usage,
-};
-
struct ConstBufferKey {
- u32 cbuf{};
- u32 offset{};
- u32 value{};
+ u32 cbuf = 0;
+ u32 offset = 0;
+ u32 value = 0;
};
struct BoundSamplerKey {
- u32 offset{};
- Tegra::Engines::SamplerDescriptor sampler{};
+ u32 offset = 0;
+ Tegra::Engines::SamplerDescriptor sampler;
};
struct BindlessSamplerKey {
- u32 cbuf{};
- u32 offset{};
- Tegra::Engines::SamplerDescriptor sampler{};
+ u32 cbuf = 0;
+ u32 offset = 0;
+ Tegra::Engines::SamplerDescriptor sampler;
};
-constexpr u32 NativeVersion = 12;
-
-// Making sure sizes doesn't change by accident
-static_assert(sizeof(ProgramVariant) == 20);
+constexpr u32 NativeVersion = 20;
ShaderCacheVersionHash GetShaderCacheVersionHash() {
ShaderCacheVersionHash hash{};
@@ -67,61 +59,124 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() {
} // Anonymous namespace
-ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, ShaderType type, ProgramCode code,
- ProgramCode code_b)
- : unique_identifier{unique_identifier}, type{type}, code{std::move(code)}, code_b{std::move(
- code_b)} {}
+ShaderDiskCacheEntry::ShaderDiskCacheEntry() = default;
-ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default;
+ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default;
-ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default;
-
-bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) {
- if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) ||
- file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
+bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
+ if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
return false;
}
- u32 code_size{};
- u32 code_size_b{};
+ u32 code_size;
+ u32 code_size_b;
if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
file.ReadBytes(&code_size_b, sizeof(u32)) != sizeof(u32)) {
return false;
}
-
code.resize(code_size);
code_b.resize(code_size_b);
- if (file.ReadArray(code.data(), code_size) != code_size)
+ if (file.ReadArray(code.data(), code_size) != code_size) {
return false;
-
+ }
if (HasProgramA() && file.ReadArray(code_b.data(), code_size_b) != code_size_b) {
return false;
}
+
+ u8 is_texture_handler_size_known;
+ u32 texture_handler_size_value;
+ u32 num_keys;
+ u32 num_bound_samplers;
+ u32 num_bindless_samplers;
+ if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 ||
+ file.ReadArray(&is_texture_handler_size_known, 1) != 1 ||
+ file.ReadArray(&texture_handler_size_value, 1) != 1 ||
+ file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 ||
+ file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 ||
+ file.ReadArray(&num_bindless_samplers, 1) != 1) {
+ return false;
+ }
+ if (is_texture_handler_size_known) {
+ texture_handler_size = texture_handler_size_value;
+ }
+
+ std::vector<ConstBufferKey> flat_keys(num_keys);
+ std::vector<BoundSamplerKey> flat_bound_samplers(num_bound_samplers);
+ std::vector<BindlessSamplerKey> flat_bindless_samplers(num_bindless_samplers);
+ if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() ||
+ file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) !=
+ flat_bound_samplers.size() ||
+ file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) !=
+ flat_bindless_samplers.size()) {
+ return false;
+ }
+ for (const auto& key : flat_keys) {
+ keys.insert({{key.cbuf, key.offset}, key.value});
+ }
+ for (const auto& key : flat_bound_samplers) {
+ bound_samplers.emplace(key.offset, key.sampler);
+ }
+ for (const auto& key : flat_bindless_samplers) {
+ bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
+ }
+
return true;
}
-bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
- if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(static_cast<u32>(type)) != 1 ||
+bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const {
+ if (file.WriteObject(static_cast<u32>(type)) != 1 ||
file.WriteObject(static_cast<u32>(code.size())) != 1 ||
file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
return false;
}
-
- if (file.WriteArray(code.data(), code.size()) != code.size())
+ if (file.WriteArray(code.data(), code.size()) != code.size()) {
return false;
-
+ }
if (HasProgramA() && file.WriteArray(code_b.data(), code_b.size()) != code_b.size()) {
return false;
}
- return true;
+
+ if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(bound_buffer) != 1 ||
+ file.WriteObject(static_cast<u8>(texture_handler_size.has_value())) != 1 ||
+ file.WriteObject(texture_handler_size.value_or(0)) != 1 ||
+ file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 ||
+ file.WriteObject(static_cast<u32>(keys.size())) != 1 ||
+ file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 ||
+ file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) {
+ return false;
+ }
+
+ std::vector<ConstBufferKey> flat_keys;
+ flat_keys.reserve(keys.size());
+ for (const auto& [address, value] : keys) {
+ flat_keys.push_back(ConstBufferKey{address.first, address.second, value});
+ }
+
+ std::vector<BoundSamplerKey> flat_bound_samplers;
+ flat_bound_samplers.reserve(bound_samplers.size());
+ for (const auto& [address, sampler] : bound_samplers) {
+ flat_bound_samplers.push_back(BoundSamplerKey{address, sampler});
+ }
+
+ std::vector<BindlessSamplerKey> flat_bindless_samplers;
+ flat_bindless_samplers.reserve(bindless_samplers.size());
+ for (const auto& [address, sampler] : bindless_samplers) {
+ flat_bindless_samplers.push_back(
+ BindlessSamplerKey{address.first, address.second, sampler});
+ }
+
+ return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() &&
+ file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) ==
+ flat_bound_samplers.size() &&
+ file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) ==
+ flat_bindless_samplers.size();
}
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
-std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
-ShaderDiskCacheOpenGL::LoadTransferable() {
+std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
// Skip games without title id
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
@@ -130,17 +185,14 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
FileUtil::IOFile file(GetTransferablePath(), "rb");
if (!file.IsOpen()) {
- LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}",
- GetTitleID());
+ LOG_INFO(Render_OpenGL, "No transferable shader cache found");
is_usable = true;
return {};
}
u32 version{};
if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
- LOG_ERROR(Render_OpenGL,
- "Failed to get transferable cache version for title id={}, skipping",
- GetTitleID());
+ LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
return {};
}
@@ -158,105 +210,42 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
}
// Version is valid, load the shaders
- constexpr const char error_loading[] = "Failed to load transferable raw entry, skipping";
- std::vector<ShaderDiskCacheRaw> raws;
- std::vector<ShaderDiskCacheUsage> usages;
+ std::vector<ShaderDiskCacheEntry> entries;
while (file.Tell() < file.GetSize()) {
- TransferableEntryKind kind{};
- if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) {
- LOG_ERROR(Render_OpenGL, "Failed to read transferable file, skipping");
- return {};
- }
-
- switch (kind) {
- case TransferableEntryKind::Raw: {
- ShaderDiskCacheRaw entry;
- if (!entry.Load(file)) {
- LOG_ERROR(Render_OpenGL, error_loading);
- return {};
- }
- transferable.insert({entry.GetUniqueIdentifier(), {}});
- raws.push_back(std::move(entry));
- break;
- }
- case TransferableEntryKind::Usage: {
- ShaderDiskCacheUsage usage;
-
- u32 num_keys{};
- u32 num_bound_samplers{};
- u32 num_bindless_samplers{};
- if (file.ReadArray(&usage.unique_identifier, 1) != 1 ||
- file.ReadArray(&usage.variant, 1) != 1 ||
- file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 ||
- file.ReadArray(&num_bound_samplers, 1) != 1 ||
- file.ReadArray(&num_bindless_samplers, 1) != 1) {
- LOG_ERROR(Render_OpenGL, error_loading);
- return {};
- }
-
- std::vector<ConstBufferKey> keys(num_keys);
- std::vector<BoundSamplerKey> bound_samplers(num_bound_samplers);
- std::vector<BindlessSamplerKey> bindless_samplers(num_bindless_samplers);
- if (file.ReadArray(keys.data(), keys.size()) != keys.size() ||
- file.ReadArray(bound_samplers.data(), bound_samplers.size()) !=
- bound_samplers.size() ||
- file.ReadArray(bindless_samplers.data(), bindless_samplers.size()) !=
- bindless_samplers.size()) {
- LOG_ERROR(Render_OpenGL, error_loading);
- return {};
- }
- for (const auto& key : keys) {
- usage.keys.insert({{key.cbuf, key.offset}, key.value});
- }
- for (const auto& key : bound_samplers) {
- usage.bound_samplers.emplace(key.offset, key.sampler);
- }
- for (const auto& key : bindless_samplers) {
- usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
- }
-
- usages.push_back(std::move(usage));
- break;
- }
- default:
- LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={}, skipping",
- static_cast<u32>(kind));
+ ShaderDiskCacheEntry& entry = entries.emplace_back();
+ if (!entry.Load(file)) {
+ LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
return {};
}
}
is_usable = true;
- return {{std::move(raws), std::move(usages)}};
+ return {std::move(entries)};
}
-std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>
-ShaderDiskCacheOpenGL::LoadPrecompiled() {
+std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled() {
if (!is_usable) {
return {};
}
- std::string path = GetPrecompiledPath();
- FileUtil::IOFile file(path, "rb");
+ FileUtil::IOFile file(GetPrecompiledPath(), "rb");
if (!file.IsOpen()) {
- LOG_INFO(Render_OpenGL, "No precompiled shader cache found for game with title id={}",
- GetTitleID());
+ LOG_INFO(Render_OpenGL, "No precompiled shader cache found");
return {};
}
- const auto result = LoadPrecompiledFile(file);
- if (!result) {
- LOG_INFO(Render_OpenGL,
- "Failed to load precompiled cache for game with title id={}, removing",
- GetTitleID());
- file.Close();
- InvalidatePrecompiled();
- return {};
+ if (const auto result = LoadPrecompiledFile(file)) {
+ return *result;
}
- return *result;
+
+ LOG_INFO(Render_OpenGL, "Failed to load precompiled cache");
+ file.Close();
+ InvalidatePrecompiled();
+ return {};
}
-std::optional<std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
-ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
+std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(
+ FileUtil::IOFile& file) {
// Read compressed file from disk and decompress to virtual precompiled cache file
std::vector<u8> compressed(file.GetSize());
file.ReadBytes(compressed.data(), compressed.size());
@@ -275,58 +264,22 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
return {};
}
- ShaderDumpsMap dumps;
+ std::vector<ShaderDiskCachePrecompiled> entries;
while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
- u32 num_keys{};
- u32 num_bound_samplers{};
- u32 num_bindless_samplers{};
- ShaderDiskCacheUsage usage;
- if (!LoadObjectFromPrecompiled(usage.unique_identifier) ||
- !LoadObjectFromPrecompiled(usage.variant) ||
- !LoadObjectFromPrecompiled(usage.bound_buffer) ||
- !LoadObjectFromPrecompiled(num_keys) ||
- !LoadObjectFromPrecompiled(num_bound_samplers) ||
- !LoadObjectFromPrecompiled(num_bindless_samplers)) {
- return {};
- }
- std::vector<ConstBufferKey> keys(num_keys);
- std::vector<BoundSamplerKey> bound_samplers(num_bound_samplers);
- std::vector<BindlessSamplerKey> bindless_samplers(num_bindless_samplers);
- if (!LoadArrayFromPrecompiled(keys.data(), keys.size()) ||
- !LoadArrayFromPrecompiled(bound_samplers.data(), bound_samplers.size()) !=
- bound_samplers.size() ||
- !LoadArrayFromPrecompiled(bindless_samplers.data(), bindless_samplers.size()) !=
- bindless_samplers.size()) {
- return {};
- }
- for (const auto& key : keys) {
- usage.keys.insert({{key.cbuf, key.offset}, key.value});
- }
- for (const auto& key : bound_samplers) {
- usage.bound_samplers.emplace(key.offset, key.sampler);
- }
- for (const auto& key : bindless_samplers) {
- usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
- }
-
- ShaderDiskCacheDump dump;
- if (!LoadObjectFromPrecompiled(dump.binary_format)) {
- return {};
- }
-
- u32 binary_length{};
- if (!LoadObjectFromPrecompiled(binary_length)) {
+ u32 binary_size;
+ auto& entry = entries.emplace_back();
+ if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
+ !LoadObjectFromPrecompiled(entry.binary_format) ||
+ !LoadObjectFromPrecompiled(binary_size)) {
return {};
}
- dump.binary.resize(binary_length);
- if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) {
+ entry.binary.resize(binary_size);
+ if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
return {};
}
-
- dumps.emplace(std::move(usage), dump);
}
- return dumps;
+ return entries;
}
void ShaderDiskCacheOpenGL::InvalidateTransferable() {
@@ -346,13 +299,13 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
}
}
-void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
+void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
if (!is_usable) {
return;
}
- const u64 id = entry.GetUniqueIdentifier();
- if (transferable.find(id) != transferable.end()) {
+ const u64 id = entry.unique_identifier;
+ if (stored_transferable.find(id) != stored_transferable.end()) {
// The shader already exists
return;
}
@@ -361,71 +314,17 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
if (!file.IsOpen()) {
return;
}
- if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) {
+ if (!entry.Save(file)) {
LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry, removing");
file.Close();
InvalidateTransferable();
return;
}
- transferable.insert({id, {}});
-}
-void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
- if (!is_usable) {
- return;
- }
-
- const auto it = transferable.find(usage.unique_identifier);
- ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously");
-
- auto& usages{it->second};
- if (usages.find(usage) != usages.end()) {
- // Skip this variant since the shader is already stored.
- return;
- }
- usages.insert(usage);
-
- FileUtil::IOFile file = AppendTransferableFile();
- if (!file.IsOpen())
- return;
- const auto Close = [&] {
- LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry, removing");
- file.Close();
- InvalidateTransferable();
- };
-
- if (file.WriteObject(TransferableEntryKind::Usage) != 1 ||
- file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 ||
- file.WriteObject(usage.bound_buffer) != 1 ||
- file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 ||
- file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 ||
- file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) {
- Close();
- return;
- }
- for (const auto& [pair, value] : usage.keys) {
- const auto [cbuf, offset] = pair;
- if (file.WriteObject(ConstBufferKey{cbuf, offset, value}) != 1) {
- Close();
- return;
- }
- }
- for (const auto& [offset, sampler] : usage.bound_samplers) {
- if (file.WriteObject(BoundSamplerKey{offset, sampler}) != 1) {
- Close();
- return;
- }
- }
- for (const auto& [pair, sampler] : usage.bindless_samplers) {
- const auto [cbuf, offset] = pair;
- if (file.WriteObject(BindlessSamplerKey{cbuf, offset, sampler}) != 1) {
- Close();
- return;
- }
- }
+ stored_transferable.insert(id);
}
-void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) {
+void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint program) {
if (!is_usable) {
return;
}
@@ -437,51 +336,19 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
SavePrecompiledHeaderToVirtualPrecompiledCache();
}
- GLint binary_length{};
+ GLint binary_length;
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length);
- GLenum binary_format{};
+ GLenum binary_format;
std::vector<u8> binary(binary_length);
glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
- const auto Close = [&] {
+ if (!SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(binary_format) ||
+ !SaveObjectToPrecompiled(static_cast<u32>(binary.size())) ||
+ !SaveArrayToPrecompiled(binary.data(), binary.size())) {
LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016X}, removing",
- usage.unique_identifier);
+ unique_identifier);
InvalidatePrecompiled();
- };
-
- if (!SaveObjectToPrecompiled(usage.unique_identifier) ||
- !SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) ||
- !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) ||
- !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) ||
- !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) {
- Close();
- return;
- }
- for (const auto& [pair, value] : usage.keys) {
- const auto [cbuf, offset] = pair;
- if (SaveObjectToPrecompiled(ConstBufferKey{cbuf, offset, value}) != 1) {
- Close();
- return;
- }
- }
- for (const auto& [offset, sampler] : usage.bound_samplers) {
- if (SaveObjectToPrecompiled(BoundSamplerKey{offset, sampler}) != 1) {
- Close();
- return;
- }
- }
- for (const auto& [pair, sampler] : usage.bindless_samplers) {
- const auto [cbuf, offset] = pair;
- if (SaveObjectToPrecompiled(BindlessSamplerKey{cbuf, offset, sampler}) != 1) {
- Close();
- return;
- }
- }
- if (!SaveObjectToPrecompiled(static_cast<u32>(binary_format)) ||
- !SaveObjectToPrecompiled(static_cast<u32>(binary_length)) ||
- !SaveArrayToPrecompiled(binary.data(), binary.size())) {
- Close();
}
}
@@ -534,7 +401,6 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) {
LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}",
precompiled_path);
- return;
}
}
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index ef2371f6d..d5be52e40 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -19,8 +19,7 @@
#include "common/common_types.h"
#include "core/file_sys/vfs_vector.h"
#include "video_core/engines/shader_type.h"
-#include "video_core/renderer_opengl/gl_shader_gen.h"
-#include "video_core/shader/const_buffer_locker.h"
+#include "video_core/shader/registry.h"
namespace Core {
class System;
@@ -32,139 +31,39 @@ class IOFile;
namespace OpenGL {
-struct ShaderDiskCacheUsage;
-struct ShaderDiskCacheDump;
-
using ProgramCode = std::vector<u64>;
-using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
-
-/// Describes the different variants a program can be compiled with.
-struct ProgramVariant final {
- ProgramVariant() = default;
-
- /// Graphics constructor.
- explicit constexpr ProgramVariant(GLenum primitive_mode) noexcept
- : primitive_mode{primitive_mode} {}
-
- /// Compute constructor.
- explicit constexpr ProgramVariant(u32 block_x, u32 block_y, u32 block_z, u32 shared_memory_size,
- u32 local_memory_size) noexcept
- : block_x{block_x}, block_y{static_cast<u16>(block_y)}, block_z{static_cast<u16>(block_z)},
- shared_memory_size{shared_memory_size}, local_memory_size{local_memory_size} {}
-
- // Graphics specific parameters.
- GLenum primitive_mode{};
-
- // Compute specific parameters.
- u32 block_x{};
- u16 block_y{};
- u16 block_z{};
- u32 shared_memory_size{};
- u32 local_memory_size{};
-
- bool operator==(const ProgramVariant& rhs) const noexcept {
- return std::tie(primitive_mode, block_x, block_y, block_z, shared_memory_size,
- local_memory_size) == std::tie(rhs.primitive_mode, rhs.block_x, rhs.block_y,
- rhs.block_z, rhs.shared_memory_size,
- rhs.local_memory_size);
- }
-
- bool operator!=(const ProgramVariant& rhs) const noexcept {
- return !operator==(rhs);
- }
-};
-static_assert(std::is_trivially_copyable_v<ProgramVariant>);
-
-/// Describes how a shader is used.
-struct ShaderDiskCacheUsage {
- u64 unique_identifier{};
- ProgramVariant variant;
- u32 bound_buffer{};
- VideoCommon::Shader::KeyMap keys;
- VideoCommon::Shader::BoundSamplerMap bound_samplers;
- VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
-
- bool operator==(const ShaderDiskCacheUsage& rhs) const {
- return std::tie(unique_identifier, variant, keys, bound_samplers, bindless_samplers) ==
- std::tie(rhs.unique_identifier, rhs.variant, rhs.keys, rhs.bound_samplers,
- rhs.bindless_samplers);
- }
-
- bool operator!=(const ShaderDiskCacheUsage& rhs) const {
- return !operator==(rhs);
- }
-};
-
-} // namespace OpenGL
-
-namespace std {
-
-template <>
-struct hash<OpenGL::ProgramVariant> {
- std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
- return (static_cast<std::size_t>(variant.primitive_mode) << 6) ^
- static_cast<std::size_t>(variant.block_x) ^
- (static_cast<std::size_t>(variant.block_y) << 32) ^
- (static_cast<std::size_t>(variant.block_z) << 48) ^
- (static_cast<std::size_t>(variant.shared_memory_size) << 16) ^
- (static_cast<std::size_t>(variant.local_memory_size) << 36);
- }
-};
-
-template <>
-struct hash<OpenGL::ShaderDiskCacheUsage> {
- std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept {
- return static_cast<std::size_t>(usage.unique_identifier) ^
- std::hash<OpenGL::ProgramVariant>{}(usage.variant);
- }
-};
-
-} // namespace std
-
-namespace OpenGL {
-/// Describes a shader how it's used by the guest GPU
-class ShaderDiskCacheRaw {
-public:
- explicit ShaderDiskCacheRaw(u64 unique_identifier, Tegra::Engines::ShaderType type,
- ProgramCode code, ProgramCode code_b = {});
- ShaderDiskCacheRaw();
- ~ShaderDiskCacheRaw();
+/// Describes a shader and how it's used by the guest GPU
+struct ShaderDiskCacheEntry {
+ ShaderDiskCacheEntry();
+ ~ShaderDiskCacheEntry();
bool Load(FileUtil::IOFile& file);
bool Save(FileUtil::IOFile& file) const;
- u64 GetUniqueIdentifier() const {
- return unique_identifier;
- }
-
bool HasProgramA() const {
return !code.empty() && !code_b.empty();
}
- Tegra::Engines::ShaderType GetType() const {
- return type;
- }
-
- const ProgramCode& GetCode() const {
- return code;
- }
-
- const ProgramCode& GetCodeB() const {
- return code_b;
- }
-
-private:
- u64 unique_identifier{};
Tegra::Engines::ShaderType type{};
ProgramCode code;
ProgramCode code_b;
+
+ u64 unique_identifier = 0;
+ std::optional<u32> texture_handler_size;
+ u32 bound_buffer = 0;
+ VideoCommon::Shader::GraphicsInfo graphics_info;
+ VideoCommon::Shader::ComputeInfo compute_info;
+ VideoCommon::Shader::KeyMap keys;
+ VideoCommon::Shader::BoundSamplerMap bound_samplers;
+ VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
};
/// Contains an OpenGL dumped binary program
-struct ShaderDiskCacheDump {
- GLenum binary_format{};
+struct ShaderDiskCachePrecompiled {
+ u64 unique_identifier = 0;
+ GLenum binary_format = 0;
std::vector<u8> binary;
};
@@ -174,11 +73,10 @@ public:
~ShaderDiskCacheOpenGL();
/// Loads transferable cache. If file has a old version or on failure, it deletes the file.
- std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
- LoadTransferable();
+ std::optional<std::vector<ShaderDiskCacheEntry>> LoadTransferable();
/// Loads current game's precompiled cache. Invalidates on failure.
- std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> LoadPrecompiled();
+ std::vector<ShaderDiskCachePrecompiled> LoadPrecompiled();
/// Removes the transferable (and precompiled) cache file.
void InvalidateTransferable();
@@ -187,21 +85,18 @@ public:
void InvalidatePrecompiled();
/// Saves a raw dump to the transferable file. Checks for collisions.
- void SaveRaw(const ShaderDiskCacheRaw& entry);
-
- /// Saves shader usage to the transferable file. Does not check for collisions.
- void SaveUsage(const ShaderDiskCacheUsage& usage);
+ void SaveEntry(const ShaderDiskCacheEntry& entry);
/// Saves a dump entry to the precompiled file. Does not check for collisions.
- void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program);
+ void SavePrecompiled(u64 unique_identifier, GLuint program);
/// Serializes virtual precompiled shader cache file to real file
void SaveVirtualPrecompiledFile();
private:
/// Loads the transferable cache. Returns empty on failure.
- std::optional<std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
- LoadPrecompiledFile(FileUtil::IOFile& file);
+ std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile(
+ FileUtil::IOFile& file);
/// Opens current game's transferable file and write it's header if it doesn't exist
FileUtil::IOFile AppendTransferableFile() const;
@@ -270,7 +165,7 @@ private:
std::size_t precompiled_cache_virtual_file_offset = 0;
// Stored transferable shaders
- std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
+ std::unordered_set<u64> stored_transferable;
// The cache has been loaded at boot
bool is_usable{};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
deleted file mode 100644
index 34946fb47..000000000
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <string>
-
-#include <fmt/format.h>
-
-#include "video_core/engines/maxwell_3d.h"
-#include "video_core/engines/shader_type.h"
-#include "video_core/renderer_opengl/gl_device.h"
-#include "video_core/renderer_opengl/gl_shader_decompiler.h"
-#include "video_core/renderer_opengl/gl_shader_gen.h"
-#include "video_core/shader/shader_ir.h"
-
-namespace OpenGL::GLShader {
-
-using Tegra::Engines::Maxwell3D;
-using Tegra::Engines::ShaderType;
-using VideoCommon::Shader::CompileDepth;
-using VideoCommon::Shader::CompilerSettings;
-using VideoCommon::Shader::ProgramCode;
-using VideoCommon::Shader::ShaderIR;
-
-std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b) {
- std::string out = GetCommonDeclarations();
- out += fmt::format(R"(
-layout (std140, binding = {}) uniform vs_config {{
- float y_direction;
-}};
-
-)",
- EmulationUniformBlockBinding);
- out += Decompile(device, ir, ShaderType::Vertex, "vertex");
- if (ir_b) {
- out += Decompile(device, *ir_b, ShaderType::Vertex, "vertex_b");
- }
-
- out += R"(
-void main() {
- gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);
- execute_vertex();
-)";
- if (ir_b) {
- out += " execute_vertex_b();";
- }
- out += "}\n";
- return out;
-}
-
-std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) {
- std::string out = GetCommonDeclarations();
- out += fmt::format(R"(
-layout (std140, binding = {}) uniform gs_config {{
- float y_direction;
-}};
-
-)",
- EmulationUniformBlockBinding);
- out += Decompile(device, ir, ShaderType::Geometry, "geometry");
-
- out += R"(
-void main() {
- execute_geometry();
-}
-)";
- return out;
-}
-
-std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir) {
- std::string out = GetCommonDeclarations();
- out += fmt::format(R"(
-layout (location = 0) out vec4 FragColor0;
-layout (location = 1) out vec4 FragColor1;
-layout (location = 2) out vec4 FragColor2;
-layout (location = 3) out vec4 FragColor3;
-layout (location = 4) out vec4 FragColor4;
-layout (location = 5) out vec4 FragColor5;
-layout (location = 6) out vec4 FragColor6;
-layout (location = 7) out vec4 FragColor7;
-
-layout (std140, binding = {}) uniform fs_config {{
- float y_direction;
-}};
-
-)",
- EmulationUniformBlockBinding);
- out += Decompile(device, ir, ShaderType::Fragment, "fragment");
-
- out += R"(
-void main() {
- execute_fragment();
-}
-)";
- return out;
-}
-
-std::string GenerateComputeShader(const Device& device, const ShaderIR& ir) {
- std::string out = GetCommonDeclarations();
- out += Decompile(device, ir, ShaderType::Compute, "compute");
- out += R"(
-void main() {
- execute_compute();
-}
-)";
- return out;
-}
-
-} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
deleted file mode 100644
index cba2be9f9..000000000
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <vector>
-
-#include "common/common_types.h"
-#include "video_core/renderer_opengl/gl_shader_decompiler.h"
-#include "video_core/shader/shader_ir.h"
-
-namespace OpenGL {
-class Device;
-}
-
-namespace OpenGL::GLShader {
-
-using VideoCommon::Shader::ProgramCode;
-using VideoCommon::Shader::ShaderIR;
-
-/// Generates the GLSL vertex shader program source code for the given VS program
-std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b);
-
-/// Generates the GLSL geometry shader program source code for the given GS program
-std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir);
-
-/// Generates the GLSL fragment shader program source code for the given FS program
-std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir);
-
-/// Generates the GLSL compute shader program source code for the given CS program
-std::string GenerateComputeShader(const Device& device, const ShaderIR& ir);
-
-} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 2d3838a7a..f424e3000 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -53,6 +53,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false}, // R8UI
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false}, // RGBA16U
+ {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, false}, // RGBA16S
{GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false}, // RGBA16UI
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false}, // RGBA32UI
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 12333e8c9..fca5e3ec0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -5,8 +5,11 @@
#include <algorithm>
#include <cstddef>
#include <cstdlib>
+#include <cstring>
#include <memory>
+
#include <glad/glad.h>
+
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -25,6 +28,8 @@
namespace OpenGL {
+namespace {
+
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
// to wait on available presentation frames.
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
@@ -41,124 +46,6 @@ struct Frame {
bool is_srgb{}; /// Framebuffer is sRGB or RGB
};
-/**
- * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
- * but also make sure that rendering happens at the pace that the frontend dictates. This is a
- * helper class that the renderer uses to sync frames between the render thread and the presentation
- * thread
- */
-class FrameMailbox {
-public:
- std::mutex swap_chain_lock;
- std::condition_variable present_cv;
- std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
- std::queue<Frame*> free_queue;
- std::deque<Frame*> present_queue;
- Frame* previous_frame{};
-
- FrameMailbox() {
- for (auto& frame : swap_chain) {
- free_queue.push(&frame);
- }
- }
-
- ~FrameMailbox() {
- // lock the mutex and clear out the present and free_queues and notify any people who are
- // blocked to prevent deadlock on shutdown
- std::scoped_lock lock{swap_chain_lock};
- std::queue<Frame*>().swap(free_queue);
- present_queue.clear();
- present_cv.notify_all();
- }
-
- void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
- frame->present.Release();
- frame->present.Create();
- GLint previous_draw_fbo{};
- glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
- frame->color.handle);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
- frame->color_reloaded = false;
- }
-
- void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
- // Recreate the color texture attachment
- frame->color.Release();
- frame->color.Create();
- const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
- glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
-
- // Recreate the FBO for the render target
- frame->render.Release();
- frame->render.Create();
- glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
- frame->color.handle);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
- }
-
- frame->width = width;
- frame->height = height;
- frame->color_reloaded = true;
- }
-
- Frame* GetRenderFrame() {
- std::unique_lock lock{swap_chain_lock};
-
- // If theres no free frames, we will reuse the oldest render frame
- if (free_queue.empty()) {
- auto frame = present_queue.back();
- present_queue.pop_back();
- return frame;
- }
-
- Frame* frame = free_queue.front();
- free_queue.pop();
- return frame;
- }
-
- void ReleaseRenderFrame(Frame* frame) {
- std::unique_lock lock{swap_chain_lock};
- present_queue.push_front(frame);
- present_cv.notify_one();
- }
-
- Frame* TryGetPresentFrame(int timeout_ms) {
- std::unique_lock lock{swap_chain_lock};
- // wait for new entries in the present_queue
- present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
- [&] { return !present_queue.empty(); });
- if (present_queue.empty()) {
- // timed out waiting for a frame to draw so return the previous frame
- return previous_frame;
- }
-
- // free the previous frame and add it back to the free queue
- if (previous_frame) {
- free_queue.push(previous_frame);
- }
-
- // the newest entries are pushed to the front of the queue
- Frame* frame = present_queue.front();
- present_queue.pop_front();
- // remove all old entries from the present queue and move them back to the free_queue
- for (auto f : present_queue) {
- free_queue.push(f);
- }
- present_queue.clear();
- previous_frame = frame;
- return frame;
- }
-};
-
-namespace {
-
constexpr char VERTEX_SHADER[] = R"(
#version 430 core
@@ -211,6 +98,24 @@ struct ScreenRectVertex {
std::array<GLfloat, 2> tex_coord;
};
+/// Returns true if any debug tool is attached
+bool HasDebugTool() {
+ const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
+ if (nsight) {
+ return true;
+ }
+
+ GLint num_extensions;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
+ for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
+ const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
+ if (!std::strcmp(name, "GL_EXT_debug_tool")) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
* corner and (width, height) on the lower-bottom.
@@ -294,6 +199,153 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
} // Anonymous namespace
+/**
+ * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
+ * but also make sure that rendering happens at the pace that the frontend dictates. This is a
+ * helper class that the renderer uses to sync frames between the render thread and the presentation
+ * thread
+ */
+class FrameMailbox {
+public:
+ std::mutex swap_chain_lock;
+ std::condition_variable present_cv;
+ std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
+ std::queue<Frame*> free_queue;
+ std::deque<Frame*> present_queue;
+ Frame* previous_frame{};
+
+ FrameMailbox() : has_debug_tool{HasDebugTool()} {
+ for (auto& frame : swap_chain) {
+ free_queue.push(&frame);
+ }
+ }
+
+ ~FrameMailbox() {
+ // lock the mutex and clear out the present and free_queues and notify any people who are
+ // blocked to prevent deadlock on shutdown
+ std::scoped_lock lock{swap_chain_lock};
+ std::queue<Frame*>().swap(free_queue);
+ present_queue.clear();
+ present_cv.notify_all();
+ }
+
+ void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
+ frame->present.Release();
+ frame->present.Create();
+ GLint previous_draw_fbo{};
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+ frame->color.handle);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
+ }
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
+ frame->color_reloaded = false;
+ }
+
+ void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
+ // Recreate the color texture attachment
+ frame->color.Release();
+ frame->color.Create();
+ const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
+ glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
+
+ // Recreate the FBO for the render target
+ frame->render.Release();
+ frame->render.Create();
+ glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+ frame->color.handle);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
+ }
+
+ frame->width = width;
+ frame->height = height;
+ frame->color_reloaded = true;
+ }
+
+ Frame* GetRenderFrame() {
+ std::unique_lock lock{swap_chain_lock};
+
+ // If theres no free frames, we will reuse the oldest render frame
+ if (free_queue.empty()) {
+ auto frame = present_queue.back();
+ present_queue.pop_back();
+ return frame;
+ }
+
+ Frame* frame = free_queue.front();
+ free_queue.pop();
+ return frame;
+ }
+
+ void ReleaseRenderFrame(Frame* frame) {
+ std::unique_lock lock{swap_chain_lock};
+ present_queue.push_front(frame);
+ present_cv.notify_one();
+
+ DebugNotifyNextFrame();
+ }
+
+ Frame* TryGetPresentFrame(int timeout_ms) {
+ DebugWaitForNextFrame();
+
+ std::unique_lock lock{swap_chain_lock};
+ // wait for new entries in the present_queue
+ present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
+ [&] { return !present_queue.empty(); });
+ if (present_queue.empty()) {
+ // timed out waiting for a frame to draw so return the previous frame
+ return previous_frame;
+ }
+
+ // free the previous frame and add it back to the free queue
+ if (previous_frame) {
+ free_queue.push(previous_frame);
+ }
+
+ // the newest entries are pushed to the front of the queue
+ Frame* frame = present_queue.front();
+ present_queue.pop_front();
+ // remove all old entries from the present queue and move them back to the free_queue
+ for (auto f : present_queue) {
+ free_queue.push(f);
+ }
+ present_queue.clear();
+ previous_frame = frame;
+ return frame;
+ }
+
+private:
+ std::mutex debug_synch_mutex;
+ std::condition_variable debug_synch_condition;
+ std::atomic_int frame_for_debug{};
+ const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step
+
+ /// Signal that a new frame is available (called from GPU thread)
+ void DebugNotifyNextFrame() {
+ if (!has_debug_tool) {
+ return;
+ }
+ frame_for_debug++;
+ std::lock_guard lock{debug_synch_mutex};
+ debug_synch_condition.notify_one();
+ }
+
+ /// Wait for a new frame to be available (called from presentation thread)
+ void DebugWaitForNextFrame() {
+ if (!has_debug_tool) {
+ return;
+ }
+ const int last_frame = frame_for_debug;
+ std::unique_lock lock{debug_synch_mutex};
+ debug_synch_condition.wait(lock,
+ [this, last_frame] { return frame_for_debug > last_frame; });
+ }
+};
+
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
frame_mailbox{std::make_unique<FrameMailbox>()} {}
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index df3ac707c..0e2e5e6c7 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -125,6 +125,7 @@ struct FormatTuple {
{vk::Format::eR8Uint, Attachable | Storage}, // R8UI
{vk::Format::eR16G16B16A16Sfloat, Attachable | Storage}, // RGBA16F
{vk::Format::eR16G16B16A16Unorm, Attachable | Storage}, // RGBA16U
+ {vk::Format::eR16G16B16A16Snorm, Attachable | Storage}, // RGBA16S
{vk::Format::eR16G16B16A16Uint, Attachable | Storage}, // RGBA16UI
{vk::Format::eB10G11R11UfloatPack32, Attachable | Storage}, // R11FG11FB10F
{vk::Format::eR32G32B32A32Uint, Attachable | Storage}, // RGBA32UI
@@ -331,6 +332,8 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
return vk::Format::eR16G16B16Unorm;
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
return vk::Format::eR16G16B16A16Unorm;
+ case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
+ return vk::Format::eA2B10G10R10UnormPack32;
default:
break;
}
@@ -364,6 +367,10 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
return vk::Format::eR8G8B8A8Uint;
case Maxwell::VertexAttribute::Size::Size_32:
return vk::Format::eR32Uint;
+ case Maxwell::VertexAttribute::Size::Size_32_32:
+ return vk::Format::eR32G32Uint;
+ case Maxwell::VertexAttribute::Size::Size_32_32_32:
+ return vk::Format::eR32G32B32Uint;
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
return vk::Format::eR32G32B32A32Uint;
default:
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 886bde3b9..28d2fbc4f 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -107,8 +107,7 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
features.occlusionQueryPrecise = true;
features.fragmentStoresAndAtomics = true;
features.shaderImageGatherExtended = true;
- features.shaderStorageImageReadWithoutFormat =
- is_shader_storage_img_read_without_format_supported;
+ features.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported;
features.shaderStorageImageWriteWithoutFormat = true;
features.textureCompressionASTC_LDR = is_optimal_astc_supported;
@@ -148,6 +147,15 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
}
+ vk::PhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
+ if (ext_transform_feedback) {
+ transform_feedback.transformFeedback = true;
+ transform_feedback.geometryStreams = true;
+ SetNext(next, transform_feedback);
+ } else {
+ LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks");
+ }
+
if (!ext_depth_range_unrestricted) {
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
}
@@ -385,7 +393,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
}
};
- extensions.reserve(14);
+ extensions.reserve(15);
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME);
extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
@@ -397,18 +405,22 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
[[maybe_unused]] const bool nsight =
std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
- bool khr_shader_float16_int8{};
- bool ext_subgroup_size_control{};
+ bool has_khr_shader_float16_int8{};
+ bool has_ext_subgroup_size_control{};
+ bool has_ext_transform_feedback{};
for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
Test(extension, khr_uniform_buffer_standard_layout,
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
- Test(extension, khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
+ Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
+ false);
Test(extension, ext_depth_range_unrestricted,
VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
Test(extension, ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
Test(extension, ext_shader_viewport_index_layer,
VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true);
- Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
+ Test(extension, has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
+ false);
+ Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
false);
if (Settings::values.renderer_debug) {
Test(extension, nv_device_diagnostic_checkpoints,
@@ -416,13 +428,13 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
}
}
- if (khr_shader_float16_int8) {
+ if (has_khr_shader_float16_int8) {
is_float16_supported =
GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16;
extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
}
- if (ext_subgroup_size_control) {
+ if (has_ext_subgroup_size_control) {
const auto features =
GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi);
const auto properties =
@@ -439,6 +451,20 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
is_warp_potentially_bigger = true;
}
+ if (has_ext_transform_feedback) {
+ const auto features =
+ GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dldi);
+ const auto properties =
+ GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dldi);
+
+ if (features.transformFeedback && features.geometryStreams &&
+ properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers &&
+ properties.transformFeedbackQueries && properties.transformFeedbackDraw) {
+ extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
+ ext_transform_feedback = true;
+ }
+ }
+
return extensions;
}
@@ -467,8 +493,7 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) {
const auto supported_features{physical.getFeatures(dldi)};
- is_shader_storage_img_read_without_format_supported =
- supported_features.shaderStorageImageReadWithoutFormat;
+ is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat;
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi);
}
@@ -510,6 +535,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
vk::Format::eR32G32Sfloat,
vk::Format::eR32G32Uint,
vk::Format::eR16G16B16A16Uint,
+ vk::Format::eR16G16B16A16Snorm,
vk::Format::eR16G16B16A16Unorm,
vk::Format::eR16G16Unorm,
vk::Format::eR16G16Snorm,
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 2c27ad730..6e656517f 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -122,11 +122,6 @@ public:
return properties.limits.maxPushConstantsSize;
}
- /// Returns true if Shader storage Image Read Without Format supported.
- bool IsShaderStorageImageReadWithoutFormatSupported() const {
- return is_shader_storage_img_read_without_format_supported;
- }
-
/// Returns true if ASTC is natively supported.
bool IsOptimalAstcSupported() const {
return is_optimal_astc_supported;
@@ -147,6 +142,11 @@ public:
return (guest_warp_stages & stage) != vk::ShaderStageFlags{};
}
+ /// Returns true if formatless image load is supported.
+ bool IsFormatlessImageLoadSupported() const {
+ return is_formatless_image_load_supported;
+ }
+
/// Returns true if the device supports VK_EXT_scalar_block_layout.
bool IsKhrUniformBufferStandardLayoutSupported() const {
return khr_uniform_buffer_standard_layout;
@@ -167,6 +167,11 @@ public:
return ext_shader_viewport_index_layer;
}
+ /// Returns true if the device supports VK_EXT_transform_feedback.
+ bool IsExtTransformFeedbackSupported() const {
+ return ext_transform_feedback;
+ }
+
/// Returns true if the device supports VK_NV_device_diagnostic_checkpoints.
bool IsNvDeviceDiagnosticCheckpoints() const {
return nv_device_diagnostic_checkpoints;
@@ -214,26 +219,26 @@ private:
static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties(
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical);
- const vk::PhysicalDevice physical; ///< Physical device.
- vk::DispatchLoaderDynamic dld; ///< Device function pointers.
- vk::PhysicalDeviceProperties properties; ///< Device properties.
- UniqueDevice logical; ///< Logical device.
- vk::Queue graphics_queue; ///< Main graphics queue.
- vk::Queue present_queue; ///< Main present queue.
- u32 graphics_family{}; ///< Main graphics queue family index.
- u32 present_family{}; ///< Main present queue family index.
- vk::DriverIdKHR driver_id{}; ///< Driver ID.
- vk::ShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.
- bool is_optimal_astc_supported{}; ///< Support for native ASTC.
- bool is_float16_supported{}; ///< Support for float16 arithmetics.
- bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
+ const vk::PhysicalDevice physical; ///< Physical device.
+ vk::DispatchLoaderDynamic dld; ///< Device function pointers.
+ vk::PhysicalDeviceProperties properties; ///< Device properties.
+ UniqueDevice logical; ///< Logical device.
+ vk::Queue graphics_queue; ///< Main graphics queue.
+ vk::Queue present_queue; ///< Main present queue.
+ u32 graphics_family{}; ///< Main graphics queue family index.
+ u32 present_family{}; ///< Main present queue family index.
+ vk::DriverIdKHR driver_id{}; ///< Driver ID.
+ vk::ShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.ed
+ bool is_optimal_astc_supported{}; ///< Support for native ASTC.
+ bool is_float16_supported{}; ///< Support for float16 arithmetics.
+ bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
+ bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
+ bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints.
- bool is_shader_storage_img_read_without_format_supported{}; ///< Support for shader storage
- ///< image read without format
// Telemetry parameters
std::string vendor_name; ///< Device's driver name.
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 144e1e007..91e7b7791 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -161,8 +161,8 @@ CachedShader::CachedShader(Core::System& system, Tegra::Engines::ShaderType stag
GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
ProgramCode program_code, u32 main_offset)
: RasterizerCacheObject{host_ptr}, gpu_addr{gpu_addr}, cpu_addr{cpu_addr},
- program_code{std::move(program_code)}, locker{stage, GetEngine(system, stage)},
- shader_ir{this->program_code, main_offset, compiler_settings, locker},
+ program_code{std::move(program_code)}, registry{stage, GetEngine(system, stage)},
+ shader_ir{this->program_code, main_offset, compiler_settings, registry},
entries{GenerateShaderEntries(shader_ir)} {}
CachedShader::~CachedShader() = default;
@@ -179,10 +179,11 @@ Tegra::Engines::ConstBufferEngineInterface& CachedShader::GetEngine(
VKPipelineCache::VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
const VKDevice& device, VKScheduler& scheduler,
VKDescriptorPool& descriptor_pool,
- VKUpdateDescriptorQueue& update_descriptor_queue)
+ VKUpdateDescriptorQueue& update_descriptor_queue,
+ VKRenderPassCache& renderpass_cache)
: RasterizerCache{rasterizer}, system{system}, device{device}, scheduler{scheduler},
descriptor_pool{descriptor_pool}, update_descriptor_queue{update_descriptor_queue},
- renderpass_cache(device) {}
+ renderpass_cache{renderpass_cache} {}
VKPipelineCache::~VKPipelineCache() = default;
@@ -273,9 +274,9 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
specialization.workgroup_size = key.workgroup_size;
specialization.shared_memory_size = key.shared_memory_size;
- const SPIRVShader spirv_shader{
- Decompile(device, shader->GetIR(), ShaderType::Compute, specialization),
- shader->GetEntries()};
+ const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute,
+ shader->GetRegistry(), specialization),
+ shader->GetEntries()};
entry = std::make_unique<VKComputePipeline>(device, scheduler, descriptor_pool,
update_descriptor_queue, spirv_shader);
return *entry;
@@ -324,8 +325,7 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
const auto& gpu = system.GPU().Maxwell3D();
Specialization specialization;
- specialization.primitive_topology = fixed_state.input_assembly.topology;
- if (specialization.primitive_topology == Maxwell::PrimitiveTopology::Points) {
+ if (fixed_state.input_assembly.topology == Maxwell::PrimitiveTopology::Points) {
ASSERT(fixed_state.input_assembly.point_size != 0.0f);
specialization.point_size = fixed_state.input_assembly.point_size;
}
@@ -333,9 +333,6 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].type;
}
specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one;
- specialization.tessellation.primitive = fixed_state.tessellation.primitive;
- specialization.tessellation.spacing = fixed_state.tessellation.spacing;
- specialization.tessellation.clockwise = fixed_state.tessellation.clockwise;
SPIRVProgram program;
std::vector<vk::DescriptorSetLayoutBinding> bindings;
@@ -356,8 +353,9 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5
const auto program_type = GetShaderType(program_enum);
const auto& entries = shader->GetEntries();
- program[stage] = {Decompile(device, shader->GetIR(), program_type, specialization),
- entries};
+ program[stage] = {
+ Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization),
+ entries};
if (program_enum == Maxwell::ShaderProgram::VertexA) {
// VertexB was combined with VertexA, so we skip the VertexB iteration
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 92a670cc7..c4c112290 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -25,7 +25,7 @@
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
-#include "video_core/shader/const_buffer_locker.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
#include "video_core/surface.h"
@@ -132,6 +132,10 @@ public:
return shader_ir;
}
+ const VideoCommon::Shader::Registry& GetRegistry() const {
+ return registry;
+ }
+
const VideoCommon::Shader::ShaderIR& GetIR() const {
return shader_ir;
}
@@ -147,7 +151,7 @@ private:
GPUVAddr gpu_addr{};
VAddr cpu_addr{};
ProgramCode program_code;
- VideoCommon::Shader::ConstBufferLocker locker;
+ VideoCommon::Shader::Registry registry;
VideoCommon::Shader::ShaderIR shader_ir;
ShaderEntries entries;
};
@@ -157,7 +161,8 @@ public:
explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
const VKDevice& device, VKScheduler& scheduler,
VKDescriptorPool& descriptor_pool,
- VKUpdateDescriptorQueue& update_descriptor_queue);
+ VKUpdateDescriptorQueue& update_descriptor_queue,
+ VKRenderPassCache& renderpass_cache);
~VKPipelineCache();
std::array<Shader, Maxwell::MaxShaderProgram> GetShaders();
@@ -180,8 +185,7 @@ private:
VKScheduler& scheduler;
VKDescriptorPool& descriptor_pool;
VKUpdateDescriptorQueue& update_descriptor_queue;
-
- VKRenderPassCache renderpass_cache;
+ VKRenderPassCache& renderpass_cache;
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 2bcb17b56..755aad643 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -287,12 +287,13 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
screen_info{screen_info}, device{device}, resource_manager{resource_manager},
memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler},
staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
- update_descriptor_queue(device, scheduler),
+ update_descriptor_queue(device, scheduler), renderpass_cache(device),
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
texture_cache(system, *this, device, resource_manager, memory_manager, scheduler,
staging_pool),
- pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue),
+ pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue,
+ renderpass_cache),
buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
sampler_cache(device), query_cache(system, *this, device, scheduler) {
scheduler.SetQueryCache(query_cache);
@@ -347,6 +348,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
[&pipeline](auto cmdbuf, auto& dld) { cmdbuf.setCheckpointNV(&pipeline, dld); });
}
+ BeginTransformFeedback();
+
const auto pipeline_layout = pipeline.GetLayout();
const auto descriptor_set = pipeline.CommitDescriptorSet();
scheduler.Record([pipeline_layout, descriptor_set, draw_params](auto cmdbuf, auto& dld) {
@@ -356,18 +359,23 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
}
draw_params.Draw(cmdbuf, dld);
});
+
+ EndTransformFeedback();
}
void RasterizerVulkan::Clear() {
MICROPROFILE_SCOPE(Vulkan_Clearing);
- query_cache.UpdateCounters();
-
const auto& gpu = system.GPU().Maxwell3D();
if (!system.GPU().Maxwell3D().ShouldExecute()) {
return;
}
+ sampled_views.clear();
+ image_views.clear();
+
+ query_cache.UpdateCounters();
+
const auto& regs = gpu.regs;
const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
regs.clear_buffers.A;
@@ -376,52 +384,54 @@ void RasterizerVulkan::Clear() {
if (!use_color && !use_depth && !use_stencil) {
return;
}
- // Clearing images requires to be out of a renderpass
- scheduler.RequestOutsideRenderPassOperationContext();
- // TODO(Rodrigo): Implement clears rendering a quad or using beginning a renderpass.
+ [[maybe_unused]] const auto texceptions = UpdateAttachments();
+ DEBUG_ASSERT(texceptions.none());
+ SetupImageTransitions(0, color_attachments, zeta_attachment);
- if (use_color) {
- View color_view;
- {
- MICROPROFILE_SCOPE(Vulkan_RenderTargets);
- color_view = texture_cache.GetColorBufferSurface(regs.clear_buffers.RT.Value(), false);
- }
+ const vk::RenderPass renderpass = renderpass_cache.GetRenderPass(GetRenderPassParams(0));
+ const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass);
+ scheduler.RequestRenderpass({renderpass, framebuffer, {{0, 0}, render_area}, 0, nullptr});
+
+ const auto& scissor = regs.scissor_test[0];
+ const vk::Offset2D scissor_offset(scissor.min_x, scissor.min_y);
+ vk::Extent2D scissor_extent{scissor.max_x - scissor.min_x, scissor.max_y - scissor.min_y};
+ scissor_extent.width = std::min(scissor_extent.width, render_area.width);
+ scissor_extent.height = std::min(scissor_extent.height, render_area.height);
- color_view->Transition(vk::ImageLayout::eTransferDstOptimal,
- vk::PipelineStageFlagBits::eTransfer,
- vk::AccessFlagBits::eTransferWrite);
+ const u32 layer = regs.clear_buffers.layer;
+ const vk::ClearRect clear_rect({scissor_offset, scissor_extent}, layer, 1);
+ if (use_color) {
const std::array clear_color = {regs.clear_color[0], regs.clear_color[1],
regs.clear_color[2], regs.clear_color[3]};
- const vk::ClearColorValue clear(clear_color);
- scheduler.Record([image = color_view->GetImage(),
- subresource = color_view->GetImageSubresourceRange(),
- clear](auto cmdbuf, auto& dld) {
- cmdbuf.clearColorImage(image, vk::ImageLayout::eTransferDstOptimal, clear, subresource,
- dld);
+ const vk::ClearValue clear_value{clear_color};
+ const u32 color_attachment = regs.clear_buffers.RT;
+ scheduler.Record([color_attachment, clear_value, clear_rect](auto cmdbuf, auto& dld) {
+ const vk::ClearAttachment attachment(vk::ImageAspectFlagBits::eColor, color_attachment,
+ clear_value);
+ cmdbuf.clearAttachments(1, &attachment, 1, &clear_rect, dld);
});
}
- if (use_depth || use_stencil) {
- View zeta_surface;
- {
- MICROPROFILE_SCOPE(Vulkan_RenderTargets);
- zeta_surface = texture_cache.GetDepthBufferSurface(false);
- }
- zeta_surface->Transition(vk::ImageLayout::eTransferDstOptimal,
- vk::PipelineStageFlagBits::eTransfer,
- vk::AccessFlagBits::eTransferWrite);
-
- const vk::ClearDepthStencilValue clear(regs.clear_depth,
- static_cast<u32>(regs.clear_stencil));
- scheduler.Record([image = zeta_surface->GetImage(),
- subresource = zeta_surface->GetImageSubresourceRange(),
- clear](auto cmdbuf, auto& dld) {
- cmdbuf.clearDepthStencilImage(image, vk::ImageLayout::eTransferDstOptimal, clear,
- subresource, dld);
- });
+ if (!use_depth && !use_stencil) {
+ return;
+ }
+ vk::ImageAspectFlags aspect_flags;
+ if (use_depth) {
+ aspect_flags |= vk::ImageAspectFlagBits::eDepth;
}
+ if (use_stencil) {
+ aspect_flags |= vk::ImageAspectFlagBits::eStencil;
+ }
+
+ scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
+ clear_rect, aspect_flags](auto cmdbuf, auto& dld) {
+ const vk::ClearDepthStencilValue clear_zeta(clear_depth, clear_stencil);
+ const vk::ClearValue clear_value{clear_zeta};
+ const vk::ClearAttachment attachment(aspect_flags, 0, clear_value);
+ cmdbuf.clearAttachments(1, &attachment, 1, &clear_rect, dld);
+ });
}
void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
@@ -738,6 +748,44 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateStencilFaces(regs);
}
+void RasterizerVulkan::BeginTransformFeedback() {
+ const auto& regs = system.GPU().Maxwell3D().regs;
+ if (regs.tfb_enabled == 0) {
+ return;
+ }
+
+ UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
+ regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
+ regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
+
+ UNIMPLEMENTED_IF(regs.tfb_bindings[1].buffer_enable);
+ UNIMPLEMENTED_IF(regs.tfb_bindings[2].buffer_enable);
+ UNIMPLEMENTED_IF(regs.tfb_bindings[3].buffer_enable);
+
+ const auto& binding = regs.tfb_bindings[0];
+ UNIMPLEMENTED_IF(binding.buffer_enable == 0);
+ UNIMPLEMENTED_IF(binding.buffer_offset != 0);
+
+ const GPUVAddr gpu_addr = binding.Address();
+ const std::size_t size = binding.buffer_size;
+ const auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true);
+
+ scheduler.Record([buffer = *buffer, offset = offset, size](auto cmdbuf, auto& dld) {
+ cmdbuf.bindTransformFeedbackBuffersEXT(0, {buffer}, {offset}, {size}, dld);
+ cmdbuf.beginTransformFeedbackEXT(0, {}, {}, dld);
+ });
+}
+
+void RasterizerVulkan::EndTransformFeedback() {
+ const auto& regs = system.GPU().Maxwell3D().regs;
+ if (regs.tfb_enabled == 0) {
+ return;
+ }
+
+ scheduler.Record(
+ [](auto cmdbuf, auto& dld) { cmdbuf.endTransformFeedbackEXT(0, {}, {}, dld); });
+}
+
void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
BufferBindings& buffer_bindings) {
const auto& regs = system.GPU().Maxwell3D().regs;
@@ -1109,7 +1157,7 @@ std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
// This implementation assumes that all attributes are used in the shader.
const GPUVAddr start{regs.vertex_array[index].StartAddress()};
const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
- DEBUG_ASSERT(end > start);
+ DEBUG_ASSERT(end >= start);
size += (end - start + 1) * regs.vertex_array[index].enable;
}
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 96ea05f0a..3185868e9 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -169,6 +169,10 @@ private:
void UpdateDynamicStates();
+ void BeginTransformFeedback();
+
+ void EndTransformFeedback();
+
bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
@@ -249,6 +253,7 @@ private:
VKStagingBufferPool staging_pool;
VKDescriptorPool descriptor_pool;
VKUpdateDescriptorQueue update_descriptor_queue;
+ VKRenderPassCache renderpass_cache;
QuadArrayPass quad_array_pass;
Uint8Pass uint8_pass;
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index cfcca5af0..51ecb5567 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -5,7 +5,9 @@
#include <functional>
#include <limits>
#include <map>
+#include <optional>
#include <type_traits>
+#include <unordered_map>
#include <utility>
#include <fmt/format.h>
@@ -24,6 +26,7 @@
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
+#include "video_core/shader/transform_feedback.h"
namespace Vulkan {
@@ -93,6 +96,12 @@ struct VertexIndices {
std::optional<u32> clip_distances;
};
+struct GenericVaryingDescription {
+ Id id = nullptr;
+ u32 first_element = 0;
+ bool is_scalar = false;
+};
+
spv::Dim GetSamplerDim(const Sampler& sampler) {
ASSERT(!sampler.IsBuffer());
switch (sampler.GetType()) {
@@ -266,9 +275,13 @@ bool IsPrecise(Operation operand) {
class SPIRVDecompiler final : public Sirit::Module {
public:
explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderType stage,
- const Specialization& specialization)
+ const Registry& registry, const Specialization& specialization)
: Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()},
- specialization{specialization} {
+ registry{registry}, specialization{specialization} {
+ if (stage != ShaderType::Compute) {
+ transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo());
+ }
+
AddCapability(spv::Capability::Shader);
AddCapability(spv::Capability::UniformAndStorageBuffer16BitAccess);
AddCapability(spv::Capability::ImageQuery);
@@ -286,6 +299,15 @@ public:
AddExtension("SPV_KHR_variable_pointers");
AddExtension("SPV_KHR_shader_draw_parameters");
+ if (!transform_feedback.empty()) {
+ if (device.IsExtTransformFeedbackSupported()) {
+ AddCapability(spv::Capability::TransformFeedback);
+ } else {
+ LOG_ERROR(Render_Vulkan, "Shader requires transform feedbacks but these are not "
+ "supported on this device");
+ }
+ }
+
if (ir.UsesLayer() || ir.UsesViewportIndex()) {
if (ir.UsesViewportIndex()) {
AddCapability(spv::Capability::MultiViewport);
@@ -296,7 +318,7 @@ public:
}
}
- if (device.IsShaderStorageImageReadWithoutFormatSupported()) {
+ if (device.IsFormatlessImageLoadSupported()) {
AddCapability(spv::Capability::StorageImageReadWithoutFormat);
}
@@ -318,25 +340,29 @@ public:
AddExecutionMode(main, spv::ExecutionMode::OutputVertices,
header.common2.threads_per_input_primitive);
break;
- case ShaderType::TesselationEval:
+ case ShaderType::TesselationEval: {
+ const auto& info = registry.GetGraphicsInfo();
AddCapability(spv::Capability::Tessellation);
AddEntryPoint(spv::ExecutionModel::TessellationEvaluation, main, "main", interfaces);
- AddExecutionMode(main, GetExecutionMode(specialization.tessellation.primitive));
- AddExecutionMode(main, GetExecutionMode(specialization.tessellation.spacing));
- AddExecutionMode(main, specialization.tessellation.clockwise
+ AddExecutionMode(main, GetExecutionMode(info.tessellation_primitive));
+ AddExecutionMode(main, GetExecutionMode(info.tessellation_spacing));
+ AddExecutionMode(main, info.tessellation_clockwise
? spv::ExecutionMode::VertexOrderCw
: spv::ExecutionMode::VertexOrderCcw);
break;
- case ShaderType::Geometry:
+ }
+ case ShaderType::Geometry: {
+ const auto& info = registry.GetGraphicsInfo();
AddCapability(spv::Capability::Geometry);
AddEntryPoint(spv::ExecutionModel::Geometry, main, "main", interfaces);
- AddExecutionMode(main, GetExecutionMode(specialization.primitive_topology));
+ AddExecutionMode(main, GetExecutionMode(info.primitive_topology));
AddExecutionMode(main, GetExecutionMode(header.common3.output_topology));
AddExecutionMode(main, spv::ExecutionMode::OutputVertices,
header.common4.max_output_vertices);
// TODO(Rodrigo): Where can we get this info from?
AddExecutionMode(main, spv::ExecutionMode::Invocations, 1U);
break;
+ }
case ShaderType::Fragment:
AddEntryPoint(spv::ExecutionModel::Fragment, main, "main", interfaces);
AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
@@ -545,7 +571,8 @@ private:
if (stage != ShaderType::Geometry) {
return;
}
- const u32 num_input = GetNumPrimitiveTopologyVertices(specialization.primitive_topology);
+ const auto& info = registry.GetGraphicsInfo();
+ const u32 num_input = GetNumPrimitiveTopologyVertices(info.primitive_topology);
DeclareInputVertexArray(num_input);
DeclareOutputVertex();
}
@@ -742,12 +769,34 @@ private:
}
void DeclareOutputAttributes() {
+ if (stage == ShaderType::Compute || stage == ShaderType::Fragment) {
+ return;
+ }
+
+ UNIMPLEMENTED_IF(registry.GetGraphicsInfo().tfb_enabled && stage != ShaderType::Vertex);
for (const auto index : ir.GetOutputAttributes()) {
if (!IsGenericAttribute(index)) {
continue;
}
- const u32 location = GetGenericAttributeLocation(index);
- Id type = t_float4;
+ DeclareOutputAttribute(index);
+ }
+ }
+
+ void DeclareOutputAttribute(Attribute::Index index) {
+ static constexpr std::string_view swizzle = "xyzw";
+
+ const u32 location = GetGenericAttributeLocation(index);
+ u8 element = 0;
+ while (element < 4) {
+ const std::size_t remainder = 4 - element;
+
+ std::size_t num_components = remainder;
+ const std::optional tfb = GetTransformFeedbackInfo(index, element);
+ if (tfb) {
+ num_components = tfb->components;
+ }
+
+ Id type = GetTypeVectorDefinitionLut(Type::Float).at(num_components - 1);
Id varying_default = v_varying_default;
if (IsOutputAttributeArray()) {
const u32 num = GetNumOutputVertices();
@@ -760,13 +809,45 @@ private:
}
type = TypePointer(spv::StorageClass::Output, type);
+ std::string name = fmt::format("out_attr{}", location);
+ if (num_components < 4 || element > 0) {
+ name = fmt::format("{}_{}", name, swizzle.substr(element, num_components));
+ }
+
const Id id = OpVariable(type, spv::StorageClass::Output, varying_default);
- Name(AddGlobalVariable(id), fmt::format("out_attr{}", location));
- output_attributes.emplace(index, id);
+ Name(AddGlobalVariable(id), name);
+
+ GenericVaryingDescription description;
+ description.id = id;
+ description.first_element = element;
+ description.is_scalar = num_components == 1;
+ for (u32 i = 0; i < num_components; ++i) {
+ const u8 offset = static_cast<u8>(static_cast<u32>(index) * 4 + element + i);
+ output_attributes.emplace(offset, description);
+ }
interfaces.push_back(id);
Decorate(id, spv::Decoration::Location, location);
+ if (element > 0) {
+ Decorate(id, spv::Decoration::Component, static_cast<u32>(element));
+ }
+ if (tfb && device.IsExtTransformFeedbackSupported()) {
+ Decorate(id, spv::Decoration::XfbBuffer, static_cast<u32>(tfb->buffer));
+ Decorate(id, spv::Decoration::XfbStride, static_cast<u32>(tfb->stride));
+ Decorate(id, spv::Decoration::Offset, static_cast<u32>(tfb->offset));
+ }
+
+ element = static_cast<u8>(static_cast<std::size_t>(element) + num_components);
+ }
+ }
+
+ std::optional<VaryingTFB> GetTransformFeedbackInfo(Attribute::Index index, u8 element = 0) {
+ const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
+ const auto it = transform_feedback.find(location);
+ if (it == transform_feedback.end()) {
+ return {};
}
+ return it->second;
}
u32 DeclareConstantBuffers(u32 binding) {
@@ -898,7 +979,7 @@ private:
u32 GetNumInputVertices() const {
switch (stage) {
case ShaderType::Geometry:
- return GetNumPrimitiveTopologyVertices(specialization.primitive_topology);
+ return GetNumPrimitiveTopologyVertices(registry.GetGraphicsInfo().primitive_topology);
case ShaderType::TesselationControl:
case ShaderType::TesselationEval:
return NumInputPatches;
@@ -1346,8 +1427,14 @@ private:
}
default:
if (IsGenericAttribute(attribute)) {
- const Id composite = output_attributes.at(attribute);
- return {ArrayPass(t_out_float, composite, {element}), Type::Float};
+ const u8 offset = static_cast<u8>(static_cast<u8>(attribute) * 4 + element);
+ const GenericVaryingDescription description = output_attributes.at(offset);
+ const Id composite = description.id;
+ std::vector<u32> indices;
+ if (!description.is_scalar) {
+ indices.push_back(element - description.first_element);
+ }
+ return {ArrayPass(t_out_float, composite, indices), Type::Float};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
static_cast<u32>(attribute));
@@ -1793,7 +1880,7 @@ private:
}
Expression ImageLoad(Operation operation) {
- if (!device.IsShaderStorageImageReadWithoutFormatSupported()) {
+ if (!device.IsFormatlessImageLoadSupported()) {
return {v_float_zero, Type::Float};
}
@@ -2258,11 +2345,11 @@ private:
std::array<Id, 4> GetTypeVectorDefinitionLut(Type type) const {
switch (type) {
case Type::Float:
- return {nullptr, t_float2, t_float3, t_float4};
+ return {t_float, t_float2, t_float3, t_float4};
case Type::Int:
- return {nullptr, t_int2, t_int3, t_int4};
+ return {t_int, t_int2, t_int3, t_int4};
case Type::Uint:
- return {nullptr, t_uint2, t_uint3, t_uint4};
+ return {t_uint, t_uint2, t_uint3, t_uint4};
default:
UNIMPLEMENTED();
return {};
@@ -2495,7 +2582,9 @@ private:
const ShaderIR& ir;
const ShaderType stage;
const Tegra::Shader::Header header;
+ const Registry& registry;
const Specialization& specialization;
+ std::unordered_map<u8, VaryingTFB> transform_feedback;
const Id t_void = Name(TypeVoid(), "void");
@@ -2584,7 +2673,7 @@ private:
Id shared_memory{};
std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{};
std::map<Attribute::Index, Id> input_attributes;
- std::map<Attribute::Index, Id> output_attributes;
+ std::unordered_map<u8, GenericVaryingDescription> output_attributes;
std::map<u32, Id> constant_buffers;
std::map<GlobalMemoryBase, Id> global_buffers;
std::map<u32, TexelBuffer> texel_buffers;
@@ -2870,8 +2959,9 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) {
}
std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
- ShaderType stage, const Specialization& specialization) {
- return SPIRVDecompiler(device, ir, stage, specialization).Assemble();
+ ShaderType stage, const VideoCommon::Shader::Registry& registry,
+ const Specialization& specialization) {
+ return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble();
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
index f5dc14d9e..ffea4709e 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
@@ -15,6 +15,7 @@
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_type.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace Vulkan {
@@ -91,17 +92,9 @@ struct Specialization final {
u32 shared_memory_size{};
// Graphics specific
- Maxwell::PrimitiveTopology primitive_topology{};
std::optional<float> point_size{};
std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
bool ndc_minus_one_to_one{};
-
- // Tessellation specific
- struct {
- Maxwell::TessellationPrimitive primitive{};
- Maxwell::TessellationSpacing spacing{};
- bool clockwise{};
- } tessellation;
};
// Old gcc versions don't consider this trivially copyable.
// static_assert(std::is_trivially_copyable_v<Specialization>);
@@ -114,6 +107,8 @@ struct SPIRVShader {
ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir);
std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
- Tegra::Engines::ShaderType stage, const Specialization& specialization);
+ Tegra::Engines::ShaderType stage,
+ const VideoCommon::Shader::Registry& registry,
+ const Specialization& specialization);
} // namespace Vulkan
diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp
deleted file mode 100644
index 0638be8cb..000000000
--- a/src/video_core/shader/const_buffer_locker.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <tuple>
-
-#include "common/common_types.h"
-#include "video_core/engines/maxwell_3d.h"
-#include "video_core/engines/shader_type.h"
-#include "video_core/shader/const_buffer_locker.h"
-
-namespace VideoCommon::Shader {
-
-using Tegra::Engines::SamplerDescriptor;
-
-ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage)
- : stage{shader_stage} {}
-
-ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage,
- Tegra::Engines::ConstBufferEngineInterface& engine)
- : stage{shader_stage}, engine{&engine} {}
-
-ConstBufferLocker::~ConstBufferLocker() = default;
-
-std::optional<u32> ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) {
- const std::pair<u32, u32> key = {buffer, offset};
- const auto iter = keys.find(key);
- if (iter != keys.end()) {
- return iter->second;
- }
- if (!engine) {
- return std::nullopt;
- }
- const u32 value = engine->AccessConstBuffer32(stage, buffer, offset);
- keys.emplace(key, value);
- return value;
-}
-
-std::optional<SamplerDescriptor> ConstBufferLocker::ObtainBoundSampler(u32 offset) {
- const u32 key = offset;
- const auto iter = bound_samplers.find(key);
- if (iter != bound_samplers.end()) {
- return iter->second;
- }
- if (!engine) {
- return std::nullopt;
- }
- const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset);
- bound_samplers.emplace(key, value);
- return value;
-}
-
-std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindlessSampler(
- u32 buffer, u32 offset) {
- const std::pair key = {buffer, offset};
- const auto iter = bindless_samplers.find(key);
- if (iter != bindless_samplers.end()) {
- return iter->second;
- }
- if (!engine) {
- return std::nullopt;
- }
- const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset);
- bindless_samplers.emplace(key, value);
- return value;
-}
-
-std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() {
- if (bound_buffer_saved) {
- return bound_buffer;
- }
- if (!engine) {
- return std::nullopt;
- }
- bound_buffer_saved = true;
- bound_buffer = engine->GetBoundBuffer();
- return bound_buffer;
-}
-
-void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) {
- keys.insert_or_assign({buffer, offset}, value);
-}
-
-void ConstBufferLocker::InsertBoundSampler(u32 offset, SamplerDescriptor sampler) {
- bound_samplers.insert_or_assign(offset, sampler);
-}
-
-void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDescriptor sampler) {
- bindless_samplers.insert_or_assign({buffer, offset}, sampler);
-}
-
-void ConstBufferLocker::SetBoundBuffer(u32 buffer) {
- bound_buffer_saved = true;
- bound_buffer = buffer;
-}
-
-bool ConstBufferLocker::IsConsistent() const {
- if (!engine) {
- return false;
- }
- return std::all_of(keys.begin(), keys.end(),
- [this](const auto& pair) {
- const auto [cbuf, offset] = pair.first;
- const auto value = pair.second;
- return value == engine->AccessConstBuffer32(stage, cbuf, offset);
- }) &&
- std::all_of(bound_samplers.begin(), bound_samplers.end(),
- [this](const auto& sampler) {
- const auto [key, value] = sampler;
- return value == engine->AccessBoundSampler(stage, key);
- }) &&
- std::all_of(bindless_samplers.begin(), bindless_samplers.end(),
- [this](const auto& sampler) {
- const auto [cbuf, offset] = sampler.first;
- const auto value = sampler.second;
- return value == engine->AccessBindlessSampler(stage, cbuf, offset);
- });
-}
-
-bool ConstBufferLocker::HasEqualKeys(const ConstBufferLocker& rhs) const {
- return std::tie(keys, bound_samplers, bindless_samplers) ==
- std::tie(rhs.keys, rhs.bound_samplers, rhs.bindless_samplers);
-}
-
-} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/const_buffer_locker.h b/src/video_core/shader/const_buffer_locker.h
deleted file mode 100644
index d3ea11087..000000000
--- a/src/video_core/shader/const_buffer_locker.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <optional>
-#include <unordered_map>
-#include "common/common_types.h"
-#include "common/hash.h"
-#include "video_core/engines/const_buffer_engine_interface.h"
-#include "video_core/engines/shader_type.h"
-#include "video_core/guest_driver.h"
-
-namespace VideoCommon::Shader {
-
-using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>;
-using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>;
-using BindlessSamplerMap =
- std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>;
-
-/**
- * The ConstBufferLocker is a class use to interface the 3D and compute engines with the shader
- * compiler. with it, the shader can obtain required data from GPU state and store it for disk
- * shader compilation.
- */
-class ConstBufferLocker {
-public:
- explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage);
-
- explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage,
- Tegra::Engines::ConstBufferEngineInterface& engine);
-
- ~ConstBufferLocker();
-
- /// Retrieves a key from the locker, if it's registered, it will give the registered value, if
- /// not it will obtain it from maxwell3d and register it.
- std::optional<u32> ObtainKey(u32 buffer, u32 offset);
-
- std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset);
-
- std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
-
- std::optional<u32> ObtainBoundBuffer();
-
- /// Inserts a key.
- void InsertKey(u32 buffer, u32 offset, u32 value);
-
- /// Inserts a bound sampler key.
- void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler);
-
- /// Inserts a bindless sampler key.
- void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler);
-
- /// Set the bound buffer for this locker.
- void SetBoundBuffer(u32 buffer);
-
- /// Checks keys and samplers against engine's current const buffers. Returns true if they are
- /// the same value, false otherwise;
- bool IsConsistent() const;
-
- /// Returns true if the keys are equal to the other ones in the locker.
- bool HasEqualKeys(const ConstBufferLocker& rhs) const;
-
- /// Gives an getter to the const buffer keys in the database.
- const KeyMap& GetKeys() const {
- return keys;
- }
-
- /// Gets samplers database.
- const BoundSamplerMap& GetBoundSamplers() const {
- return bound_samplers;
- }
-
- /// Gets bindless samplers database.
- const BindlessSamplerMap& GetBindlessSamplers() const {
- return bindless_samplers;
- }
-
- /// Gets bound buffer used on this shader
- u32 GetBoundBuffer() const {
- return bound_buffer;
- }
-
- /// Obtains access to the guest driver's profile.
- VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const {
- if (engine) {
- return &engine->AccessGuestDriverProfile();
- }
- return nullptr;
- }
-
-private:
- const Tegra::Engines::ShaderType stage;
- Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
- KeyMap keys;
- BoundSamplerMap bound_samplers;
- BindlessSamplerMap bindless_samplers;
- bool bound_buffer_saved{};
- u32 bound_buffer{};
-};
-
-} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 0229733b6..2e2711350 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -13,6 +13,7 @@
#include "common/common_types.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/control_flow.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
@@ -64,11 +65,11 @@ struct BlockInfo {
};
struct CFGRebuildState {
- explicit CFGRebuildState(const ProgramCode& program_code, u32 start, ConstBufferLocker& locker)
- : program_code{program_code}, locker{locker}, start{start} {}
+ explicit CFGRebuildState(const ProgramCode& program_code, u32 start, Registry& registry)
+ : program_code{program_code}, registry{registry}, start{start} {}
const ProgramCode& program_code;
- ConstBufferLocker& locker;
+ Registry& registry;
u32 start{};
std::vector<BlockInfo> block_info;
std::list<u32> inspect_queries;
@@ -438,7 +439,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
const s32 pc_target = offset + result.relative_position;
std::vector<CaseBranch> branches;
for (u32 i = 0; i < result.entries; i++) {
- auto key = state.locker.ObtainKey(result.buffer, result.offset + i * 4);
+ auto key = state.registry.ObtainKey(result.buffer, result.offset + i * 4);
if (!key) {
return {ParseResult::AbnormalFlow, parse_info};
}
@@ -656,14 +657,14 @@ void DecompileShader(CFGRebuildState& state) {
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
const CompilerSettings& settings,
- ConstBufferLocker& locker) {
+ Registry& registry) {
auto result_out = std::make_unique<ShaderCharacteristics>();
if (settings.depth == CompileDepth::BruteForce) {
result_out->settings.depth = CompileDepth::BruteForce;
return result_out;
}
- CFGRebuildState state{program_code, start_address, locker};
+ CFGRebuildState state{program_code, start_address, registry};
// Inspect Code and generate blocks
state.labels.clear();
state.labels.emplace(start_address);
diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h
index 5304998b9..62a3510d8 100644
--- a/src/video_core/shader/control_flow.h
+++ b/src/video_core/shader/control_flow.h
@@ -12,6 +12,7 @@
#include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/compiler_settings.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
@@ -111,6 +112,6 @@ struct ShaderCharacteristics {
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
const CompilerSettings& settings,
- ConstBufferLocker& locker);
+ Registry& registry);
} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index 6b697ed5d..87ac9ac6c 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -34,13 +34,9 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
return (absolute_offset % SchedPeriod) == 0;
}
-void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver,
+void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile& gpu_driver,
const std::list<Sampler>& used_samplers) {
- if (gpu_driver == nullptr) {
- LOG_CRITICAL(HW_GPU, "GPU driver profile has not been created yet");
- return;
- }
- if (gpu_driver->TextureHandlerSizeKnown() || used_samplers.size() <= 1) {
+ if (gpu_driver.IsTextureHandlerSizeKnown() || used_samplers.size() <= 1) {
return;
}
u32 count{};
@@ -53,17 +49,13 @@ void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver,
bound_offsets.emplace_back(sampler.GetOffset());
}
if (count > 1) {
- gpu_driver->DeduceTextureHandlerSize(std::move(bound_offsets));
+ gpu_driver.DeduceTextureHandlerSize(std::move(bound_offsets));
}
}
std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
- VideoCore::GuestDriverProfile* gpu_driver,
+ VideoCore::GuestDriverProfile& gpu_driver,
const std::list<Sampler>& used_samplers) {
- if (gpu_driver == nullptr) {
- LOG_CRITICAL(HW_GPU, "GPU Driver profile has not been created yet");
- return std::nullopt;
- }
const u32 base_offset = sampler_to_deduce.GetOffset();
u32 max_offset{std::numeric_limits<u32>::max()};
for (const auto& sampler : used_samplers) {
@@ -77,7 +69,7 @@ std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
if (max_offset == std::numeric_limits<u32>::max()) {
return std::nullopt;
}
- return ((max_offset - base_offset) * 4) / gpu_driver->GetTextureHandlerSize();
+ return ((max_offset - base_offset) * 4) / gpu_driver.GetTextureHandlerSize();
}
} // Anonymous namespace
@@ -149,7 +141,7 @@ void ShaderIR::Decode() {
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
decompiled = false;
- auto info = ScanFlow(program_code, main_offset, settings, locker);
+ auto info = ScanFlow(program_code, main_offset, settings, registry);
auto& shader_info = *info;
coverage_begin = shader_info.start;
coverage_end = shader_info.end;
@@ -364,7 +356,7 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) {
void ShaderIR::PostDecode() {
// Deduce texture handler size if needed
- auto gpu_driver = locker.AccessGuestDriverProfile();
+ auto gpu_driver = registry.AccessGuestDriverProfile();
DeduceTextureHandlerSize(gpu_driver, used_samplers);
// Deduce Indexed Samplers
if (!uses_indexed_samplers) {
diff --git a/src/video_core/shader/decode/bfe.cpp b/src/video_core/shader/decode/bfe.cpp
index e02bcd097..8e3b46e8e 100644
--- a/src/video_core/shader/decode/bfe.cpp
+++ b/src/video_core/shader/decode/bfe.cpp
@@ -17,33 +17,60 @@ u32 ShaderIR::DecodeBfe(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
const auto opcode = OpCode::Decode(instr);
- UNIMPLEMENTED_IF(instr.bfe.negate_b);
-
Node op_a = GetRegister(instr.gpr8);
- op_a = GetOperandAbsNegInteger(op_a, false, instr.bfe.negate_a, false);
-
- switch (opcode->get().GetId()) {
- case OpCode::Id::BFE_IMM: {
- UNIMPLEMENTED_IF_MSG(instr.generates_cc,
- "Condition codes generation in BFE is not implemented");
+ Node op_b = [&] {
+ switch (opcode->get().GetId()) {
+ case OpCode::Id::BFE_R:
+ return GetRegister(instr.gpr20);
+ case OpCode::Id::BFE_C:
+ return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
+ case OpCode::Id::BFE_IMM:
+ return Immediate(instr.alu.GetSignedImm20_20());
+ default:
+ UNREACHABLE();
+ return Immediate(0);
+ }
+ }();
- const Node inner_shift_imm = Immediate(static_cast<u32>(instr.bfe.GetLeftShiftValue()));
- const Node outer_shift_imm =
- Immediate(static_cast<u32>(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position));
+ UNIMPLEMENTED_IF_MSG(instr.bfe.rd_cc, "Condition codes in BFE is not implemented");
- const Node inner_shift =
- Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, op_a, inner_shift_imm);
- const Node outer_shift =
- Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, inner_shift, outer_shift_imm);
+ const bool is_signed = instr.bfe.is_signed;
- SetInternalFlagsFromInteger(bb, outer_shift, instr.generates_cc);
- SetRegister(bb, instr.gpr0, outer_shift);
- break;
- }
- default:
- UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName());
+ // using reverse parallel method in
+ // https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
+ // note for later if possible to implement faster method.
+ if (instr.bfe.brev) {
+ const auto swap = [&](u32 s, u32 mask) {
+ Node v1 =
+ SignedOperation(OperationCode::ILogicalShiftRight, is_signed, op_a, Immediate(s));
+ if (mask != 0) {
+ v1 = SignedOperation(OperationCode::IBitwiseAnd, is_signed, std::move(v1),
+ Immediate(mask));
+ }
+ Node v2 = op_a;
+ if (mask != 0) {
+ v2 = SignedOperation(OperationCode::IBitwiseAnd, is_signed, std::move(v2),
+ Immediate(mask));
+ }
+ v2 = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed, std::move(v2),
+ Immediate(s));
+ return SignedOperation(OperationCode::IBitwiseOr, is_signed, std::move(v1),
+ std::move(v2));
+ };
+ op_a = swap(1, 0x55555555U);
+ op_a = swap(2, 0x33333333U);
+ op_a = swap(4, 0x0F0F0F0FU);
+ op_a = swap(8, 0x00FF00FFU);
+ op_a = swap(16, 0);
}
+ const auto offset = SignedOperation(OperationCode::IBitfieldExtract, is_signed, op_b,
+ Immediate(0), Immediate(8));
+ const auto bits = SignedOperation(OperationCode::IBitfieldExtract, is_signed, op_b,
+ Immediate(8), Immediate(8));
+ auto result = SignedOperation(OperationCode::IBitfieldExtract, is_signed, op_a, offset, bits);
+ SetRegister(bb, instr.gpr0, std::move(result));
+
return pc;
}
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index bee7d8cad..48350e042 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -12,6 +12,7 @@
#include "common/logging/log.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/node_helper.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
@@ -359,8 +360,8 @@ ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sample
if (sampler_info) {
return *sampler_info;
}
- const auto sampler =
- buffer ? locker.ObtainBindlessSampler(*buffer, offset) : locker.ObtainBoundSampler(offset);
+ const auto sampler = buffer ? registry.ObtainBindlessSampler(*buffer, offset)
+ : registry.ObtainBoundSampler(offset);
if (!sampler) {
LOG_WARNING(HW_GPU, "Unknown sampler info");
return SamplerInfo{TextureType::Texture2D, false, false, false};
diff --git a/src/video_core/shader/node_helper.cpp b/src/video_core/shader/node_helper.cpp
index b3dcd291c..76c56abb5 100644
--- a/src/video_core/shader/node_helper.cpp
+++ b/src/video_core/shader/node_helper.cpp
@@ -68,6 +68,8 @@ OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed)
return OperationCode::UBitwiseXor;
case OperationCode::IBitwiseNot:
return OperationCode::UBitwiseNot;
+ case OperationCode::IBitfieldExtract:
+ return OperationCode::UBitfieldExtract;
case OperationCode::IBitfieldInsert:
return OperationCode::UBitfieldInsert;
case OperationCode::IBitCount:
diff --git a/src/video_core/shader/registry.cpp b/src/video_core/shader/registry.cpp
new file mode 100644
index 000000000..af70b3f35
--- /dev/null
+++ b/src/video_core/shader/registry.cpp
@@ -0,0 +1,161 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <tuple>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "video_core/engines/kepler_compute.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/engines/shader_type.h"
+#include "video_core/shader/registry.h"
+
+namespace VideoCommon::Shader {
+
+using Tegra::Engines::ConstBufferEngineInterface;
+using Tegra::Engines::SamplerDescriptor;
+using Tegra::Engines::ShaderType;
+
+namespace {
+
+GraphicsInfo MakeGraphicsInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) {
+ if (shader_stage == ShaderType::Compute) {
+ return {};
+ }
+ auto& graphics = static_cast<Tegra::Engines::Maxwell3D&>(engine);
+
+ GraphicsInfo info;
+ info.tfb_layouts = graphics.regs.tfb_layouts;
+ info.tfb_varying_locs = graphics.regs.tfb_varying_locs;
+ info.primitive_topology = graphics.regs.draw.topology;
+ info.tessellation_primitive = graphics.regs.tess_mode.prim;
+ info.tessellation_spacing = graphics.regs.tess_mode.spacing;
+ info.tfb_enabled = graphics.regs.tfb_enabled;
+ info.tessellation_clockwise = graphics.regs.tess_mode.cw;
+ return info;
+}
+
+ComputeInfo MakeComputeInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) {
+ if (shader_stage != ShaderType::Compute) {
+ return {};
+ }
+ auto& compute = static_cast<Tegra::Engines::KeplerCompute&>(engine);
+ const auto& launch = compute.launch_description;
+
+ ComputeInfo info;
+ info.workgroup_size = {launch.block_dim_x, launch.block_dim_y, launch.block_dim_z};
+ info.local_memory_size_in_words = launch.local_pos_alloc;
+ info.shared_memory_size_in_words = launch.shared_alloc;
+ return info;
+}
+
+} // Anonymous namespace
+
+Registry::Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info)
+ : stage{shader_stage}, stored_guest_driver_profile{info.guest_driver_profile},
+ bound_buffer{info.bound_buffer}, graphics_info{info.graphics}, compute_info{info.compute} {}
+
+Registry::Registry(Tegra::Engines::ShaderType shader_stage,
+ Tegra::Engines::ConstBufferEngineInterface& engine)
+ : stage{shader_stage}, engine{&engine}, bound_buffer{engine.GetBoundBuffer()},
+ graphics_info{MakeGraphicsInfo(shader_stage, engine)}, compute_info{MakeComputeInfo(
+ shader_stage, engine)} {}
+
+Registry::~Registry() = default;
+
+std::optional<u32> Registry::ObtainKey(u32 buffer, u32 offset) {
+ const std::pair<u32, u32> key = {buffer, offset};
+ const auto iter = keys.find(key);
+ if (iter != keys.end()) {
+ return iter->second;
+ }
+ if (!engine) {
+ return std::nullopt;
+ }
+ const u32 value = engine->AccessConstBuffer32(stage, buffer, offset);
+ keys.emplace(key, value);
+ return value;
+}
+
+std::optional<SamplerDescriptor> Registry::ObtainBoundSampler(u32 offset) {
+ const u32 key = offset;
+ const auto iter = bound_samplers.find(key);
+ if (iter != bound_samplers.end()) {
+ return iter->second;
+ }
+ if (!engine) {
+ return std::nullopt;
+ }
+ const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset);
+ bound_samplers.emplace(key, value);
+ return value;
+}
+
+std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer,
+ u32 offset) {
+ const std::pair key = {buffer, offset};
+ const auto iter = bindless_samplers.find(key);
+ if (iter != bindless_samplers.end()) {
+ return iter->second;
+ }
+ if (!engine) {
+ return std::nullopt;
+ }
+ const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset);
+ bindless_samplers.emplace(key, value);
+ return value;
+}
+
+void Registry::InsertKey(u32 buffer, u32 offset, u32 value) {
+ keys.insert_or_assign({buffer, offset}, value);
+}
+
+void Registry::InsertBoundSampler(u32 offset, SamplerDescriptor sampler) {
+ bound_samplers.insert_or_assign(offset, sampler);
+}
+
+void Registry::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDescriptor sampler) {
+ bindless_samplers.insert_or_assign({buffer, offset}, sampler);
+}
+
+bool Registry::IsConsistent() const {
+ if (!engine) {
+ return true;
+ }
+ return std::all_of(keys.begin(), keys.end(),
+ [this](const auto& pair) {
+ const auto [cbuf, offset] = pair.first;
+ const auto value = pair.second;
+ return value == engine->AccessConstBuffer32(stage, cbuf, offset);
+ }) &&
+ std::all_of(bound_samplers.begin(), bound_samplers.end(),
+ [this](const auto& sampler) {
+ const auto [key, value] = sampler;
+ return value == engine->AccessBoundSampler(stage, key);
+ }) &&
+ std::all_of(bindless_samplers.begin(), bindless_samplers.end(),
+ [this](const auto& sampler) {
+ const auto [cbuf, offset] = sampler.first;
+ const auto value = sampler.second;
+ return value == engine->AccessBindlessSampler(stage, cbuf, offset);
+ });
+}
+
+bool Registry::HasEqualKeys(const Registry& rhs) const {
+ return std::tie(keys, bound_samplers, bindless_samplers) ==
+ std::tie(rhs.keys, rhs.bound_samplers, rhs.bindless_samplers);
+}
+
+const GraphicsInfo& Registry::GetGraphicsInfo() const {
+ ASSERT(stage != Tegra::Engines::ShaderType::Compute);
+ return graphics_info;
+}
+
+const ComputeInfo& Registry::GetComputeInfo() const {
+ ASSERT(stage == Tegra::Engines::ShaderType::Compute);
+ return compute_info;
+}
+
+} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/registry.h b/src/video_core/shader/registry.h
new file mode 100644
index 000000000..0c80d35fd
--- /dev/null
+++ b/src/video_core/shader/registry.h
@@ -0,0 +1,137 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <optional>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+
+#include "common/common_types.h"
+#include "common/hash.h"
+#include "video_core/engines/const_buffer_engine_interface.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/engines/shader_type.h"
+#include "video_core/guest_driver.h"
+
+namespace VideoCommon::Shader {
+
+using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>;
+using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>;
+using BindlessSamplerMap =
+ std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>;
+
+struct GraphicsInfo {
+ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
+
+ std::array<Maxwell::TransformFeedbackLayout, Maxwell::NumTransformFeedbackBuffers>
+ tfb_layouts{};
+ std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> tfb_varying_locs{};
+ Maxwell::PrimitiveTopology primitive_topology{};
+ Maxwell::TessellationPrimitive tessellation_primitive{};
+ Maxwell::TessellationSpacing tessellation_spacing{};
+ bool tfb_enabled = false;
+ bool tessellation_clockwise = false;
+};
+static_assert(std::is_trivially_copyable_v<GraphicsInfo> &&
+ std::is_standard_layout_v<GraphicsInfo>);
+
+struct ComputeInfo {
+ std::array<u32, 3> workgroup_size{};
+ u32 shared_memory_size_in_words = 0;
+ u32 local_memory_size_in_words = 0;
+};
+static_assert(std::is_trivially_copyable_v<ComputeInfo> && std::is_standard_layout_v<ComputeInfo>);
+
+struct SerializedRegistryInfo {
+ VideoCore::GuestDriverProfile guest_driver_profile;
+ u32 bound_buffer = 0;
+ GraphicsInfo graphics;
+ ComputeInfo compute;
+};
+
+/**
+ * The Registry is a class use to interface the 3D and compute engines with the shader compiler.
+ * With it, the shader can obtain required data from GPU state and store it for disk shader
+ * compilation.
+ */
+class Registry {
+public:
+ explicit Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info);
+
+ explicit Registry(Tegra::Engines::ShaderType shader_stage,
+ Tegra::Engines::ConstBufferEngineInterface& engine);
+
+ ~Registry();
+
+ /// Retrieves a key from the registry, if it's registered, it will give the registered value, if
+ /// not it will obtain it from maxwell3d and register it.
+ std::optional<u32> ObtainKey(u32 buffer, u32 offset);
+
+ std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset);
+
+ std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
+
+ /// Inserts a key.
+ void InsertKey(u32 buffer, u32 offset, u32 value);
+
+ /// Inserts a bound sampler key.
+ void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler);
+
+ /// Inserts a bindless sampler key.
+ void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler);
+
+ /// Checks keys and samplers against engine's current const buffers.
+ /// Returns true if they are the same value, false otherwise.
+ bool IsConsistent() const;
+
+ /// Returns true if the keys are equal to the other ones in the registry.
+ bool HasEqualKeys(const Registry& rhs) const;
+
+ /// Returns graphics information from this shader
+ const GraphicsInfo& GetGraphicsInfo() const;
+
+ /// Returns compute information from this shader
+ const ComputeInfo& GetComputeInfo() const;
+
+ /// Gives an getter to the const buffer keys in the database.
+ const KeyMap& GetKeys() const {
+ return keys;
+ }
+
+ /// Gets samplers database.
+ const BoundSamplerMap& GetBoundSamplers() const {
+ return bound_samplers;
+ }
+
+ /// Gets bindless samplers database.
+ const BindlessSamplerMap& GetBindlessSamplers() const {
+ return bindless_samplers;
+ }
+
+ /// Gets bound buffer used on this shader
+ u32 GetBoundBuffer() const {
+ return bound_buffer;
+ }
+
+ /// Obtains access to the guest driver's profile.
+ VideoCore::GuestDriverProfile& AccessGuestDriverProfile() {
+ return engine ? engine->AccessGuestDriverProfile() : stored_guest_driver_profile;
+ }
+
+private:
+ const Tegra::Engines::ShaderType stage;
+ VideoCore::GuestDriverProfile stored_guest_driver_profile;
+ Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
+ KeyMap keys;
+ BoundSamplerMap bound_samplers;
+ BindlessSamplerMap bindless_samplers;
+ u32 bound_buffer;
+ GraphicsInfo graphics_info;
+ ComputeInfo compute_info;
+};
+
+} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 3a5d280a9..425927777 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -11,6 +11,7 @@
#include "common/logging/log.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/node_helper.h"
+#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
@@ -24,8 +25,8 @@ using Tegra::Shader::PredOperation;
using Tegra::Shader::Register;
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSettings settings,
- ConstBufferLocker& locker)
- : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} {
+ Registry& registry)
+ : program_code{program_code}, main_offset{main_offset}, settings{settings}, registry{registry} {
Decode();
PostDecode();
}
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index b0851c3be..dde036b40 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -18,8 +18,8 @@
#include "video_core/engines/shader_header.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/compiler_settings.h"
-#include "video_core/shader/const_buffer_locker.h"
#include "video_core/shader/node.h"
+#include "video_core/shader/registry.h"
namespace VideoCommon::Shader {
@@ -69,7 +69,7 @@ struct GlobalMemoryUsage {
class ShaderIR final {
public:
explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSettings settings,
- ConstBufferLocker& locker);
+ Registry& registry);
~ShaderIR();
const std::map<u32, NodeBlock>& GetBasicBlocks() const {
@@ -414,7 +414,7 @@ private:
const ProgramCode& program_code;
const u32 main_offset;
const CompilerSettings settings;
- ConstBufferLocker& locker;
+ Registry& registry;
bool decompiled{};
bool disable_flow_stack{};
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index 15e22b9fa..10739b37d 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -81,26 +81,20 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons
MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
return {tracked, track};
} else if (const auto operation = std::get_if<OperationNode>(&*offset)) {
- auto bound_buffer = locker.ObtainBoundBuffer();
- if (!bound_buffer) {
+ const u32 bound_buffer = registry.GetBoundBuffer();
+ if (bound_buffer != cbuf->GetIndex()) {
return {};
}
- if (*bound_buffer != cbuf->GetIndex()) {
- return {};
- }
- auto pair = DecoupleIndirectRead(*operation);
+ const auto pair = DecoupleIndirectRead(*operation);
if (!pair) {
return {};
}
auto [gpr, base_offset] = *pair;
const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
- auto gpu_driver = locker.AccessGuestDriverProfile();
- if (gpu_driver == nullptr) {
- return {};
- }
+ const auto& gpu_driver = registry.AccessGuestDriverProfile();
const u32 bindless_cv = NewCustomVariable();
- const Node op = Operation(OperationCode::UDiv, NO_PRECISE, gpr,
- Immediate(gpu_driver->GetTextureHandlerSize()));
+ const Node op =
+ Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize()));
const Node cv_node = GetCustomVariable(bindless_cv);
Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
diff --git a/src/video_core/shader/transform_feedback.cpp b/src/video_core/shader/transform_feedback.cpp
new file mode 100644
index 000000000..22a933761
--- /dev/null
+++ b/src/video_core/shader/transform_feedback.cpp
@@ -0,0 +1,115 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <unordered_map>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/shader/registry.h"
+#include "video_core/shader/transform_feedback.h"
+
+namespace VideoCommon::Shader {
+
+namespace {
+
+using Maxwell = Tegra::Engines::Maxwell3D::Regs;
+
+// TODO(Rodrigo): Change this to constexpr std::unordered_set in C++20
+
+/// Attribute offsets that describe a vector
+constexpr std::array VECTORS = {
+ 28, // gl_Position
+ 32, // Generic 0
+ 36, // Generic 1
+ 40, // Generic 2
+ 44, // Generic 3
+ 48, // Generic 4
+ 52, // Generic 5
+ 56, // Generic 6
+ 60, // Generic 7
+ 64, // Generic 8
+ 68, // Generic 9
+ 72, // Generic 10
+ 76, // Generic 11
+ 80, // Generic 12
+ 84, // Generic 13
+ 88, // Generic 14
+ 92, // Generic 15
+ 96, // Generic 16
+ 100, // Generic 17
+ 104, // Generic 18
+ 108, // Generic 19
+ 112, // Generic 20
+ 116, // Generic 21
+ 120, // Generic 22
+ 124, // Generic 23
+ 128, // Generic 24
+ 132, // Generic 25
+ 136, // Generic 26
+ 140, // Generic 27
+ 144, // Generic 28
+ 148, // Generic 29
+ 152, // Generic 30
+ 156, // Generic 31
+ 160, // gl_FrontColor
+ 164, // gl_FrontSecondaryColor
+ 160, // gl_BackColor
+ 164, // gl_BackSecondaryColor
+ 192, // gl_TexCoord[0]
+ 196, // gl_TexCoord[1]
+ 200, // gl_TexCoord[2]
+ 204, // gl_TexCoord[3]
+ 208, // gl_TexCoord[4]
+ 212, // gl_TexCoord[5]
+ 216, // gl_TexCoord[6]
+ 220, // gl_TexCoord[7]
+};
+} // namespace
+
+std::unordered_map<u8, VaryingTFB> BuildTransformFeedback(const GraphicsInfo& info) {
+
+ std::unordered_map<u8, VaryingTFB> tfb;
+
+ for (std::size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) {
+ const auto& locations = info.tfb_varying_locs[buffer];
+ const auto& layout = info.tfb_layouts[buffer];
+ const std::size_t varying_count = layout.varying_count;
+
+ std::size_t highest = 0;
+
+ for (std::size_t offset = 0; offset < varying_count; ++offset) {
+ const std::size_t base_offset = offset;
+ const u8 location = locations[offset];
+
+ VaryingTFB varying;
+ varying.buffer = layout.stream;
+ varying.stride = layout.stride;
+ varying.offset = offset * sizeof(u32);
+ varying.components = 1;
+
+ if (std::find(VECTORS.begin(), VECTORS.end(), location / 4 * 4) != VECTORS.end()) {
+ UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB");
+
+ const u8 base_index = location / 4;
+ while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) {
+ ++offset;
+ ++varying.components;
+ }
+ }
+
+ [[maybe_unused]] const bool inserted = tfb.emplace(location, varying).second;
+ UNIMPLEMENTED_IF_MSG(!inserted, "Varying already stored");
+
+ highest = std::max(highest, (base_offset + varying.components) * sizeof(u32));
+ }
+
+ UNIMPLEMENTED_IF(highest != layout.stride);
+ }
+ return tfb;
+}
+
+} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/transform_feedback.h b/src/video_core/shader/transform_feedback.h
new file mode 100644
index 000000000..77d05f64c
--- /dev/null
+++ b/src/video_core/shader/transform_feedback.h
@@ -0,0 +1,23 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "video_core/shader/registry.h"
+
+namespace VideoCommon::Shader {
+
+struct VaryingTFB {
+ std::size_t buffer;
+ std::size_t stride;
+ std::size_t offset;
+ std::size_t components;
+};
+
+std::unordered_map<u8, VaryingTFB> BuildTransformFeedback(const GraphicsInfo& info);
+
+} // namespace VideoCommon::Shader
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 9707c353d..cc7181229 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -111,6 +111,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
return PixelFormat::RGBA16F;
case Tegra::RenderTargetFormat::RGBA16_UNORM:
return PixelFormat::RGBA16U;
+ case Tegra::RenderTargetFormat::RGBA16_SNORM:
+ return PixelFormat::RGBA16S;
case Tegra::RenderTargetFormat::RGBA16_UINT:
return PixelFormat::RGBA16UI;
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index d88109e5a..ae8817465 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -25,82 +25,83 @@ enum class PixelFormat {
R8UI = 7,
RGBA16F = 8,
RGBA16U = 9,
- RGBA16UI = 10,
- R11FG11FB10F = 11,
- RGBA32UI = 12,
- DXT1 = 13,
- DXT23 = 14,
- DXT45 = 15,
- DXN1 = 16, // This is also known as BC4
- DXN2UNORM = 17,
- DXN2SNORM = 18,
- BC7U = 19,
- BC6H_UF16 = 20,
- BC6H_SF16 = 21,
- ASTC_2D_4X4 = 22,
- BGRA8 = 23,
- RGBA32F = 24,
- RG32F = 25,
- R32F = 26,
- R16F = 27,
- R16U = 28,
- R16S = 29,
- R16UI = 30,
- R16I = 31,
- RG16 = 32,
- RG16F = 33,
- RG16UI = 34,
- RG16I = 35,
- RG16S = 36,
- RGB32F = 37,
- RGBA8_SRGB = 38,
- RG8U = 39,
- RG8S = 40,
- RG32UI = 41,
- RGBX16F = 42,
- R32UI = 43,
- R32I = 44,
- ASTC_2D_8X8 = 45,
- ASTC_2D_8X5 = 46,
- ASTC_2D_5X4 = 47,
- BGRA8_SRGB = 48,
- DXT1_SRGB = 49,
- DXT23_SRGB = 50,
- DXT45_SRGB = 51,
- BC7U_SRGB = 52,
- R4G4B4A4U = 53,
- ASTC_2D_4X4_SRGB = 54,
- ASTC_2D_8X8_SRGB = 55,
- ASTC_2D_8X5_SRGB = 56,
- ASTC_2D_5X4_SRGB = 57,
- ASTC_2D_5X5 = 58,
- ASTC_2D_5X5_SRGB = 59,
- ASTC_2D_10X8 = 60,
- ASTC_2D_10X8_SRGB = 61,
- ASTC_2D_6X6 = 62,
- ASTC_2D_6X6_SRGB = 63,
- ASTC_2D_10X10 = 64,
- ASTC_2D_10X10_SRGB = 65,
- ASTC_2D_12X12 = 66,
- ASTC_2D_12X12_SRGB = 67,
- ASTC_2D_8X6 = 68,
- ASTC_2D_8X6_SRGB = 69,
- ASTC_2D_6X5 = 70,
- ASTC_2D_6X5_SRGB = 71,
- E5B9G9R9F = 72,
+ RGBA16S = 10,
+ RGBA16UI = 11,
+ R11FG11FB10F = 12,
+ RGBA32UI = 13,
+ DXT1 = 14,
+ DXT23 = 15,
+ DXT45 = 16,
+ DXN1 = 17, // This is also known as BC4
+ DXN2UNORM = 18,
+ DXN2SNORM = 19,
+ BC7U = 20,
+ BC6H_UF16 = 21,
+ BC6H_SF16 = 22,
+ ASTC_2D_4X4 = 23,
+ BGRA8 = 24,
+ RGBA32F = 25,
+ RG32F = 26,
+ R32F = 27,
+ R16F = 28,
+ R16U = 29,
+ R16S = 30,
+ R16UI = 31,
+ R16I = 32,
+ RG16 = 33,
+ RG16F = 34,
+ RG16UI = 35,
+ RG16I = 36,
+ RG16S = 37,
+ RGB32F = 38,
+ RGBA8_SRGB = 39,
+ RG8U = 40,
+ RG8S = 41,
+ RG32UI = 42,
+ RGBX16F = 43,
+ R32UI = 44,
+ R32I = 45,
+ ASTC_2D_8X8 = 46,
+ ASTC_2D_8X5 = 47,
+ ASTC_2D_5X4 = 48,
+ BGRA8_SRGB = 49,
+ DXT1_SRGB = 50,
+ DXT23_SRGB = 51,
+ DXT45_SRGB = 52,
+ BC7U_SRGB = 53,
+ R4G4B4A4U = 54,
+ ASTC_2D_4X4_SRGB = 55,
+ ASTC_2D_8X8_SRGB = 56,
+ ASTC_2D_8X5_SRGB = 57,
+ ASTC_2D_5X4_SRGB = 58,
+ ASTC_2D_5X5 = 59,
+ ASTC_2D_5X5_SRGB = 60,
+ ASTC_2D_10X8 = 61,
+ ASTC_2D_10X8_SRGB = 62,
+ ASTC_2D_6X6 = 63,
+ ASTC_2D_6X6_SRGB = 64,
+ ASTC_2D_10X10 = 65,
+ ASTC_2D_10X10_SRGB = 66,
+ ASTC_2D_12X12 = 67,
+ ASTC_2D_12X12_SRGB = 68,
+ ASTC_2D_8X6 = 69,
+ ASTC_2D_8X6_SRGB = 70,
+ ASTC_2D_6X5 = 71,
+ ASTC_2D_6X5_SRGB = 72,
+ E5B9G9R9F = 73,
MaxColorFormat,
// Depth formats
- Z32F = 73,
- Z16 = 74,
+ Z32F = 74,
+ Z16 = 75,
MaxDepthFormat,
// DepthStencil formats
- Z24S8 = 75,
- S8Z24 = 76,
- Z32FS8 = 77,
+ Z24S8 = 76,
+ S8Z24 = 77,
+ Z32FS8 = 78,
MaxDepthStencilFormat,
@@ -138,6 +139,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{
0, // R8UI
0, // RGBA16F
0, // RGBA16U
+ 0, // RGBA16S
0, // RGBA16UI
0, // R11FG11FB10F
0, // RGBA32UI
@@ -235,6 +237,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
1, // R8UI
1, // RGBA16F
1, // RGBA16U
+ 1, // RGBA16S
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
@@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
1, // R8UI
1, // RGBA16F
1, // RGBA16U
+ 1, // RGBA16S
1, // RGBA16UI
1, // R11FG11FB10F
1, // RGBA32UI
@@ -413,6 +417,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
8, // R8UI
64, // RGBA16F
64, // RGBA16U
+ 64, // RGBA16S
64, // RGBA16UI
32, // R11FG11FB10F
128, // RGBA32UI
@@ -517,6 +522,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table
SurfaceCompression::None, // R8UI
SurfaceCompression::None, // RGBA16F
SurfaceCompression::None, // RGBA16U
+ SurfaceCompression::None, // RGBA16S
SurfaceCompression::None, // RGBA16UI
SurfaceCompression::None, // R11FG11FB10F
SurfaceCompression::None, // RGBA32UI
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index cc3ad8417..e151c26c4 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -41,7 +41,7 @@ struct Table {
ComponentType alpha_component;
bool is_srgb;
};
-constexpr std::array<Table, 75> DefinitionTable = {{
+constexpr std::array<Table, 76> DefinitionTable = {{
{TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U},
{TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S},
{TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI},
@@ -61,6 +61,7 @@ constexpr std::array<Table, 75> DefinitionTable = {{
{TextureFormat::G8R8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RG8U},
{TextureFormat::G8R8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::RG8S},
+ {TextureFormat::R16_G16_B16_A16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::RGBA16S},
{TextureFormat::R16_G16_B16_A16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RGBA16U},
{TextureFormat::R16_G16_B16_A16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RGBA16F},
{TextureFormat::R16_G16_B16_A16, C, UINT, UINT, UINT, UINT, PixelFormat::RGBA16UI},
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index f00839313..9931c5ef7 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -113,8 +113,10 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
params.height = tic.Height();
params.depth = tic.Depth();
params.pitch = params.is_tiled ? 0 : tic.Pitch();
- if (params.target == SurfaceTarget::TextureCubemap ||
- params.target == SurfaceTarget::TextureCubeArray) {
+ if (params.target == SurfaceTarget::Texture2D && params.depth > 1) {
+ params.depth = 1;
+ } else if (params.target == SurfaceTarget::TextureCubemap ||
+ params.target == SurfaceTarget::TextureCubeArray) {
params.depth *= 6;
}
params.num_levels = tic.max_mip_level + 1;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 51373b687..6cdbe63d0 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -104,6 +104,11 @@ public:
if (!cache_addr) {
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
+
+ if (!IsTypeCompatible(tic.texture_type, entry)) {
+ return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
+ }
+
const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)};
const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false);
if (guard_samplers) {
@@ -914,13 +919,15 @@ private:
params.width = 1;
params.height = 1;
params.depth = 1;
+ if (target == SurfaceTarget::TextureCubemap || target == SurfaceTarget::TextureCubeArray) {
+ params.depth = 6;
+ }
params.pitch = 4;
params.num_levels = 1;
params.emulated_levels = 1;
- params.pixel_format = VideoCore::Surface::PixelFormat::RGBA16F;
+ params.pixel_format = VideoCore::Surface::PixelFormat::R8U;
params.type = VideoCore::Surface::SurfaceType::ColorTexture;
auto surface = CreateSurface(0ULL, params);
- invalid_memory.clear();
invalid_memory.resize(surface->GetHostSizeInBytes(), 0U);
surface->UploadTexture(invalid_memory);
surface->MarkAsModified(false, Tick());
@@ -1082,6 +1089,36 @@ private:
return siblings_table[static_cast<std::size_t>(format)];
}
+ /// Returns true the shader sampler entry is compatible with the TIC texture type.
+ static bool IsTypeCompatible(Tegra::Texture::TextureType tic_type,
+ const VideoCommon::Shader::Sampler& entry) {
+ const auto shader_type = entry.GetType();
+ switch (tic_type) {
+ case Tegra::Texture::TextureType::Texture1D:
+ case Tegra::Texture::TextureType::Texture1DArray:
+ return shader_type == Tegra::Shader::TextureType::Texture1D;
+ case Tegra::Texture::TextureType::Texture1DBuffer:
+ // TODO(Rodrigo): Assume as valid for now
+ return true;
+ case Tegra::Texture::TextureType::Texture2D:
+ case Tegra::Texture::TextureType::Texture2DNoMipmap:
+ return shader_type == Tegra::Shader::TextureType::Texture2D;
+ case Tegra::Texture::TextureType::Texture2DArray:
+ return shader_type == Tegra::Shader::TextureType::Texture2D ||
+ shader_type == Tegra::Shader::TextureType::TextureCube;
+ case Tegra::Texture::TextureType::Texture3D:
+ return shader_type == Tegra::Shader::TextureType::Texture3D;
+ case Tegra::Texture::TextureType::TextureCubeArray:
+ case Tegra::Texture::TextureType::TextureCubemap:
+ if (shader_type == Tegra::Shader::TextureType::TextureCube) {
+ return true;
+ }
+ return shader_type == Tegra::Shader::TextureType::Texture2D && entry.IsArray();
+ }
+ UNREACHABLE();
+ return true;
+ }
+
struct FramebufferTargetInfo {
TSurface target;
TView view;
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 33bd31865..404708d92 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -17,26 +17,37 @@
#include <algorithm>
#include <cassert>
-#include <cstdint>
#include <cstring>
#include <vector>
+#include "common/common_types.h"
+
#include "video_core/textures/astc.h"
+namespace {
+
+/// Count the number of bits set in a number.
+constexpr u32 Popcnt(u32 n) {
+ u32 c = 0;
+ for (; n; c++) {
+ n &= n - 1;
+ }
+ return c;
+}
+
+} // Anonymous namespace
+
class InputBitStream {
public:
- explicit InputBitStream(const unsigned char* ptr, int start_offset = 0)
+ explicit InputBitStream(const u8* ptr, std::size_t start_offset = 0)
: m_CurByte(ptr), m_NextBit(start_offset % 8) {}
- ~InputBitStream() = default;
-
- int GetBitsRead() const {
+ std::size_t GetBitsRead() const {
return m_BitsRead;
}
- int ReadBit() {
-
- int bit = *m_CurByte >> m_NextBit++;
+ u32 ReadBit() {
+ u32 bit = *m_CurByte >> m_NextBit++;
while (m_NextBit >= 8) {
m_NextBit -= 8;
m_CurByte++;
@@ -46,57 +57,66 @@ public:
return bit & 1;
}
- unsigned int ReadBits(unsigned int nBits) {
- unsigned int ret = 0;
- for (unsigned int i = 0; i < nBits; i++) {
+ u32 ReadBits(std::size_t nBits) {
+ u32 ret = 0;
+ for (std::size_t i = 0; i < nBits; ++i) {
+ ret |= (ReadBit() & 1) << i;
+ }
+ return ret;
+ }
+
+ template <std::size_t nBits>
+ u32 ReadBits() {
+ u32 ret = 0;
+ for (std::size_t i = 0; i < nBits; ++i) {
ret |= (ReadBit() & 1) << i;
}
return ret;
}
private:
- const unsigned char* m_CurByte;
- int m_NextBit = 0;
- int m_BitsRead = 0;
+ const u8* m_CurByte;
+ std::size_t m_NextBit = 0;
+ std::size_t m_BitsRead = 0;
};
class OutputBitStream {
public:
- explicit OutputBitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0)
+ explicit OutputBitStream(u8* ptr, s32 nBits = 0, s32 start_offset = 0)
: m_NumBits(nBits), m_CurByte(ptr), m_NextBit(start_offset % 8) {}
~OutputBitStream() = default;
- int GetBitsWritten() const {
+ s32 GetBitsWritten() const {
return m_BitsWritten;
}
- void WriteBitsR(unsigned int val, unsigned int nBits) {
- for (unsigned int i = 0; i < nBits; i++) {
+ void WriteBitsR(u32 val, u32 nBits) {
+ for (u32 i = 0; i < nBits; i++) {
WriteBit((val >> (nBits - i - 1)) & 1);
}
}
- void WriteBits(unsigned int val, unsigned int nBits) {
- for (unsigned int i = 0; i < nBits; i++) {
+ void WriteBits(u32 val, u32 nBits) {
+ for (u32 i = 0; i < nBits; i++) {
WriteBit((val >> i) & 1);
}
}
private:
- void WriteBit(int b) {
+ void WriteBit(s32 b) {
if (done)
return;
- const unsigned int mask = 1 << m_NextBit++;
+ const u32 mask = 1 << m_NextBit++;
// clear the bit
- *m_CurByte &= static_cast<unsigned char>(~mask);
+ *m_CurByte &= static_cast<u8>(~mask);
// Write the bit, if necessary
if (b)
- *m_CurByte |= static_cast<unsigned char>(mask);
+ *m_CurByte |= static_cast<u8>(mask);
// Next byte?
if (m_NextBit >= 8) {
@@ -107,10 +127,10 @@ private:
done = done || ++m_BitsWritten >= m_NumBits;
}
- int m_BitsWritten = 0;
- const int m_NumBits;
- unsigned char* m_CurByte;
- int m_NextBit = 0;
+ s32 m_BitsWritten = 0;
+ const s32 m_NumBits;
+ u8* m_CurByte;
+ s32 m_NextBit = 0;
bool done = false;
};
@@ -123,20 +143,20 @@ public:
Bits(const Bits&) = delete;
Bits& operator=(const Bits&) = delete;
- uint8_t operator[](uint32_t bitPos) const {
- return static_cast<uint8_t>((m_Bits >> bitPos) & 1);
+ u8 operator[](u32 bitPos) const {
+ return static_cast<u8>((m_Bits >> bitPos) & 1);
}
- IntType operator()(uint32_t start, uint32_t end) const {
+ IntType operator()(u32 start, u32 end) const {
if (start == end) {
return (*this)[start];
} else if (start > end) {
- uint32_t t = start;
+ u32 t = start;
start = end;
end = t;
}
- uint64_t mask = (1 << (end - start + 1)) - 1;
+ u64 mask = (1 << (end - start + 1)) - 1;
return (m_Bits >> start) & static_cast<IntType>(mask);
}
@@ -144,273 +164,236 @@ private:
const IntType& m_Bits;
};
-enum EIntegerEncoding { eIntegerEncoding_JustBits, eIntegerEncoding_Quint, eIntegerEncoding_Trit };
-
-class IntegerEncodedValue {
-private:
- const EIntegerEncoding m_Encoding;
- const uint32_t m_NumBits;
- uint32_t m_BitValue;
- union {
- uint32_t m_QuintValue;
- uint32_t m_TritValue;
- };
+enum class IntegerEncoding { JustBits, Qus32, Trit };
-public:
- // Jank, but we're not doing any heavy lifting in this class, so it's
- // probably OK. It allows us to use these in std::vectors...
- IntegerEncodedValue& operator=(const IntegerEncodedValue& other) {
- new (this) IntegerEncodedValue(other);
- return *this;
- }
+struct IntegerEncodedValue {
+ constexpr IntegerEncodedValue() = default;
- IntegerEncodedValue(EIntegerEncoding encoding, uint32_t numBits)
- : m_Encoding(encoding), m_NumBits(numBits) {}
+ constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
+ : encoding{encoding_}, num_bits{num_bits_} {}
- EIntegerEncoding GetEncoding() const {
- return m_Encoding;
- }
- uint32_t BaseBitLength() const {
- return m_NumBits;
- }
-
- uint32_t GetBitValue() const {
- return m_BitValue;
- }
- void SetBitValue(uint32_t val) {
- m_BitValue = val;
- }
-
- uint32_t GetTritValue() const {
- return m_TritValue;
- }
- void SetTritValue(uint32_t val) {
- m_TritValue = val;
- }
-
- uint32_t GetQuintValue() const {
- return m_QuintValue;
- }
- void SetQuintValue(uint32_t val) {
- m_QuintValue = val;
- }
-
- bool MatchesEncoding(const IntegerEncodedValue& other) const {
- return m_Encoding == other.m_Encoding && m_NumBits == other.m_NumBits;
+ constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
+ return encoding == other.encoding && num_bits == other.num_bits;
}
// Returns the number of bits required to encode nVals values.
- uint32_t GetBitLength(uint32_t nVals) const {
- uint32_t totalBits = m_NumBits * nVals;
- if (m_Encoding == eIntegerEncoding_Trit) {
+ u32 GetBitLength(u32 nVals) const {
+ u32 totalBits = num_bits * nVals;
+ if (encoding == IntegerEncoding::Trit) {
totalBits += (nVals * 8 + 4) / 5;
- } else if (m_Encoding == eIntegerEncoding_Quint) {
+ } else if (encoding == IntegerEncoding::Qus32) {
totalBits += (nVals * 7 + 2) / 3;
}
return totalBits;
}
- // Count the number of bits set in a number.
- static inline uint32_t Popcnt(uint32_t n) {
- uint32_t c;
- for (c = 0; n; c++) {
- n &= n - 1;
+ IntegerEncoding encoding{};
+ u32 num_bits = 0;
+ u32 bit_value = 0;
+ union {
+ u32 qus32_value = 0;
+ u32 trit_value;
+ };
+};
+
+static void DecodeTritBlock(InputBitStream& bits, std::vector<IntegerEncodedValue>& result,
+ u32 nBitsPerValue) {
+ // Implement the algorithm in section C.2.12
+ u32 m[5];
+ u32 t[5];
+ u32 T;
+
+ // Read the trit encoded block according to
+ // table C.2.14
+ m[0] = bits.ReadBits(nBitsPerValue);
+ T = bits.ReadBits<2>();
+ m[1] = bits.ReadBits(nBitsPerValue);
+ T |= bits.ReadBits<2>() << 2;
+ m[2] = bits.ReadBits(nBitsPerValue);
+ T |= bits.ReadBit() << 4;
+ m[3] = bits.ReadBits(nBitsPerValue);
+ T |= bits.ReadBits<2>() << 5;
+ m[4] = bits.ReadBits(nBitsPerValue);
+ T |= bits.ReadBit() << 7;
+
+ u32 C = 0;
+
+ Bits<u32> Tb(T);
+ if (Tb(2, 4) == 7) {
+ C = (Tb(5, 7) << 2) | Tb(0, 1);
+ t[4] = t[3] = 2;
+ } else {
+ C = Tb(0, 4);
+ if (Tb(5, 6) == 3) {
+ t[4] = 2;
+ t[3] = Tb[7];
+ } else {
+ t[4] = Tb[7];
+ t[3] = Tb(5, 6);
}
- return c;
}
- // Returns a new instance of this struct that corresponds to the
- // can take no more than maxval values
- static IntegerEncodedValue CreateEncoding(uint32_t maxVal) {
- while (maxVal > 0) {
- uint32_t check = maxVal + 1;
-
- // Is maxVal a power of two?
- if (!(check & (check - 1))) {
- return IntegerEncodedValue(eIntegerEncoding_JustBits, Popcnt(maxVal));
- }
-
- // Is maxVal of the type 3*2^n - 1?
- if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
- return IntegerEncodedValue(eIntegerEncoding_Trit, Popcnt(check / 3 - 1));
- }
+ Bits<u32> Cb(C);
+ if (Cb(0, 1) == 3) {
+ t[2] = 2;
+ t[1] = Cb[4];
+ t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]);
+ } else if (Cb(2, 3) == 3) {
+ t[2] = 2;
+ t[1] = 2;
+ t[0] = Cb(0, 1);
+ } else {
+ t[2] = Cb[4];
+ t[1] = Cb(2, 3);
+ t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]);
+ }
- // Is maxVal of the type 5*2^n - 1?
- if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
- return IntegerEncodedValue(eIntegerEncoding_Quint, Popcnt(check / 5 - 1));
- }
+ for (std::size_t i = 0; i < 5; ++i) {
+ IntegerEncodedValue& val = result.emplace_back(IntegerEncoding::Trit, nBitsPerValue);
+ val.bit_value = m[i];
+ val.trit_value = t[i];
+ }
+}
- // Apparently it can't be represented with a bounded integer sequence...
- // just iterate.
- maxVal--;
+static void DecodeQus32Block(InputBitStream& bits, std::vector<IntegerEncodedValue>& result,
+ u32 nBitsPerValue) {
+ // Implement the algorithm in section C.2.12
+ u32 m[3];
+ u32 q[3];
+ u32 Q;
+
+ // Read the trit encoded block according to
+ // table C.2.15
+ m[0] = bits.ReadBits(nBitsPerValue);
+ Q = bits.ReadBits<3>();
+ m[1] = bits.ReadBits(nBitsPerValue);
+ Q |= bits.ReadBits<2>() << 3;
+ m[2] = bits.ReadBits(nBitsPerValue);
+ Q |= bits.ReadBits<2>() << 5;
+
+ Bits<u32> Qb(Q);
+ if (Qb(1, 2) == 3 && Qb(5, 6) == 0) {
+ q[0] = q[1] = 4;
+ q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]);
+ } else {
+ u32 C = 0;
+ if (Qb(1, 2) == 3) {
+ q[2] = 4;
+ C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0];
+ } else {
+ q[2] = Qb(5, 6);
+ C = Qb(0, 4);
}
- return IntegerEncodedValue(eIntegerEncoding_JustBits, 0);
- }
-
- // Fills result with the values that are encoded in the given
- // bitstream. We must know beforehand what the maximum possible
- // value is, and how many values we're decoding.
- static void DecodeIntegerSequence(std::vector<IntegerEncodedValue>& result,
- InputBitStream& bits, uint32_t maxRange, uint32_t nValues) {
- // Determine encoding parameters
- IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(maxRange);
-
- // Start decoding
- uint32_t nValsDecoded = 0;
- while (nValsDecoded < nValues) {
- switch (val.GetEncoding()) {
- case eIntegerEncoding_Quint:
- DecodeQuintBlock(bits, result, val.BaseBitLength());
- nValsDecoded += 3;
- break;
- case eIntegerEncoding_Trit:
- DecodeTritBlock(bits, result, val.BaseBitLength());
- nValsDecoded += 5;
- break;
-
- case eIntegerEncoding_JustBits:
- val.SetBitValue(bits.ReadBits(val.BaseBitLength()));
- result.push_back(val);
- nValsDecoded++;
- break;
- }
+ Bits<u32> Cb(C);
+ if (Cb(0, 2) == 5) {
+ q[1] = 4;
+ q[0] = Cb(3, 4);
+ } else {
+ q[1] = Cb(3, 4);
+ q[0] = Cb(0, 2);
}
}
-private:
- static void DecodeTritBlock(InputBitStream& bits, std::vector<IntegerEncodedValue>& result,
- uint32_t nBitsPerValue) {
- // Implement the algorithm in section C.2.12
- uint32_t m[5];
- uint32_t t[5];
- uint32_t T;
-
- // Read the trit encoded block according to
- // table C.2.14
- m[0] = bits.ReadBits(nBitsPerValue);
- T = bits.ReadBits(2);
- m[1] = bits.ReadBits(nBitsPerValue);
- T |= bits.ReadBits(2) << 2;
- m[2] = bits.ReadBits(nBitsPerValue);
- T |= bits.ReadBit() << 4;
- m[3] = bits.ReadBits(nBitsPerValue);
- T |= bits.ReadBits(2) << 5;
- m[4] = bits.ReadBits(nBitsPerValue);
- T |= bits.ReadBit() << 7;
-
- uint32_t C = 0;
-
- Bits<uint32_t> Tb(T);
- if (Tb(2, 4) == 7) {
- C = (Tb(5, 7) << 2) | Tb(0, 1);
- t[4] = t[3] = 2;
- } else {
- C = Tb(0, 4);
- if (Tb(5, 6) == 3) {
- t[4] = 2;
- t[3] = Tb[7];
- } else {
- t[4] = Tb[7];
- t[3] = Tb(5, 6);
- }
+ for (std::size_t i = 0; i < 3; ++i) {
+ IntegerEncodedValue& val = result.emplace_back(IntegerEncoding::Qus32, nBitsPerValue);
+ val.bit_value = m[i];
+ val.qus32_value = q[i];
+ }
+}
+
+// Returns a new instance of this struct that corresponds to the
+// can take no more than maxval values
+static constexpr IntegerEncodedValue CreateEncoding(u32 maxVal) {
+ while (maxVal > 0) {
+ u32 check = maxVal + 1;
+
+ // Is maxVal a power of two?
+ if (!(check & (check - 1))) {
+ return IntegerEncodedValue(IntegerEncoding::JustBits, Popcnt(maxVal));
}
- Bits<uint32_t> Cb(C);
- if (Cb(0, 1) == 3) {
- t[2] = 2;
- t[1] = Cb[4];
- t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]);
- } else if (Cb(2, 3) == 3) {
- t[2] = 2;
- t[1] = 2;
- t[0] = Cb(0, 1);
- } else {
- t[2] = Cb[4];
- t[1] = Cb(2, 3);
- t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]);
+ // Is maxVal of the type 3*2^n - 1?
+ if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
+ return IntegerEncodedValue(IntegerEncoding::Trit, Popcnt(check / 3 - 1));
}
- for (uint32_t i = 0; i < 5; i++) {
- IntegerEncodedValue val(eIntegerEncoding_Trit, nBitsPerValue);
- val.SetBitValue(m[i]);
- val.SetTritValue(t[i]);
- result.push_back(val);
+ // Is maxVal of the type 5*2^n - 1?
+ if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
+ return IntegerEncodedValue(IntegerEncoding::Qus32, Popcnt(check / 5 - 1));
}
+
+ // Apparently it can't be represented with a bounded integer sequence...
+ // just iterate.
+ maxVal--;
}
+ return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
+}
- static void DecodeQuintBlock(InputBitStream& bits, std::vector<IntegerEncodedValue>& result,
- uint32_t nBitsPerValue) {
- // Implement the algorithm in section C.2.12
- uint32_t m[3];
- uint32_t q[3];
- uint32_t Q;
-
- // Read the trit encoded block according to
- // table C.2.15
- m[0] = bits.ReadBits(nBitsPerValue);
- Q = bits.ReadBits(3);
- m[1] = bits.ReadBits(nBitsPerValue);
- Q |= bits.ReadBits(2) << 3;
- m[2] = bits.ReadBits(nBitsPerValue);
- Q |= bits.ReadBits(2) << 5;
-
- Bits<uint32_t> Qb(Q);
- if (Qb(1, 2) == 3 && Qb(5, 6) == 0) {
- q[0] = q[1] = 4;
- q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]);
- } else {
- uint32_t C = 0;
- if (Qb(1, 2) == 3) {
- q[2] = 4;
- C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0];
- } else {
- q[2] = Qb(5, 6);
- C = Qb(0, 4);
- }
+static constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
+ std::array<IntegerEncodedValue, 256> encodings{};
+ for (std::size_t i = 0; i < encodings.size(); ++i) {
+ encodings[i] = CreateEncoding(static_cast<u32>(i));
+ }
+ return encodings;
+}
- Bits<uint32_t> Cb(C);
- if (Cb(0, 2) == 5) {
- q[1] = 4;
- q[0] = Cb(3, 4);
- } else {
- q[1] = Cb(3, 4);
- q[0] = Cb(0, 2);
- }
- }
+static constexpr std::array EncodingsValues = MakeEncodedValues();
+
+// Fills result with the values that are encoded in the given
+// bitstream. We must know beforehand what the maximum possible
+// value is, and how many values we're decoding.
+static void DecodeIntegerSequence(std::vector<IntegerEncodedValue>& result, InputBitStream& bits,
+ u32 maxRange, u32 nValues) {
+ // Determine encoding parameters
+ IntegerEncodedValue val = EncodingsValues[maxRange];
+
+ // Start decoding
+ u32 nValsDecoded = 0;
+ while (nValsDecoded < nValues) {
+ switch (val.encoding) {
+ case IntegerEncoding::Qus32:
+ DecodeQus32Block(bits, result, val.num_bits);
+ nValsDecoded += 3;
+ break;
+
+ case IntegerEncoding::Trit:
+ DecodeTritBlock(bits, result, val.num_bits);
+ nValsDecoded += 5;
+ break;
- for (uint32_t i = 0; i < 3; i++) {
- IntegerEncodedValue val(eIntegerEncoding_Quint, nBitsPerValue);
- val.m_BitValue = m[i];
- val.m_QuintValue = q[i];
+ case IntegerEncoding::JustBits:
+ val.bit_value = bits.ReadBits(val.num_bits);
result.push_back(val);
+ nValsDecoded++;
+ break;
}
}
-};
+}
namespace ASTCC {
struct TexelWeightParams {
- uint32_t m_Width = 0;
- uint32_t m_Height = 0;
+ u32 m_Width = 0;
+ u32 m_Height = 0;
bool m_bDualPlane = false;
- uint32_t m_MaxWeight = 0;
+ u32 m_MaxWeight = 0;
bool m_bError = false;
bool m_bVoidExtentLDR = false;
bool m_bVoidExtentHDR = false;
- uint32_t GetPackedBitSize() const {
+ u32 GetPackedBitSize() const {
// How many indices do we have?
- uint32_t nIdxs = m_Height * m_Width;
+ u32 nIdxs = m_Height * m_Width;
if (m_bDualPlane) {
nIdxs *= 2;
}
- return IntegerEncodedValue::CreateEncoding(m_MaxWeight).GetBitLength(nIdxs);
+ return EncodingsValues[m_MaxWeight].GetBitLength(nIdxs);
}
- uint32_t GetNumWeightValues() const {
- uint32_t ret = m_Width * m_Height;
+ u32 GetNumWeightValues() const {
+ u32 ret = m_Width * m_Height;
if (m_bDualPlane) {
ret *= 2;
}
@@ -422,7 +405,7 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
TexelWeightParams params;
// Read the entire block mode all at once
- uint16_t modeBits = static_cast<uint16_t>(strm.ReadBits(11));
+ u16 modeBits = static_cast<u16>(strm.ReadBits<11>());
// Does this match the void extent block mode?
if ((modeBits & 0x01FF) == 0x1FC) {
@@ -457,7 +440,7 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
// of the block mode. Layout is determined by a number
// between 0 and 9 corresponding to table C.2.8 of the
// ASTC spec.
- uint32_t layout = 0;
+ u32 layout = 0;
if ((modeBits & 0x1) || (modeBits & 0x2)) {
// layout is in [0-4]
@@ -509,7 +492,7 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
assert(layout < 10);
// Determine R
- uint32_t R = !!(modeBits & 0x10);
+ u32 R = !!(modeBits & 0x10);
if (layout < 5) {
R |= (modeBits & 0x3) << 1;
} else {
@@ -520,54 +503,54 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
// Determine width & height
switch (layout) {
case 0: {
- uint32_t A = (modeBits >> 5) & 0x3;
- uint32_t B = (modeBits >> 7) & 0x3;
+ u32 A = (modeBits >> 5) & 0x3;
+ u32 B = (modeBits >> 7) & 0x3;
params.m_Width = B + 4;
params.m_Height = A + 2;
break;
}
case 1: {
- uint32_t A = (modeBits >> 5) & 0x3;
- uint32_t B = (modeBits >> 7) & 0x3;
+ u32 A = (modeBits >> 5) & 0x3;
+ u32 B = (modeBits >> 7) & 0x3;
params.m_Width = B + 8;
params.m_Height = A + 2;
break;
}
case 2: {
- uint32_t A = (modeBits >> 5) & 0x3;
- uint32_t B = (modeBits >> 7) & 0x3;
+ u32 A = (modeBits >> 5) & 0x3;
+ u32 B = (modeBits >> 7) & 0x3;
params.m_Width = A + 2;
params.m_Height = B + 8;
break;
}
case 3: {
- uint32_t A = (modeBits >> 5) & 0x3;
- uint32_t B = (modeBits >> 7) & 0x1;
+ u32 A = (modeBits >> 5) & 0x3;
+ u32 B = (modeBits >> 7) & 0x1;
params.m_Width = A + 2;
params.m_Height = B + 6;
break;
}
case 4: {
- uint32_t A = (modeBits >> 5) & 0x3;
- uint32_t B = (modeBits >> 7) & 0x1;
+ u32 A = (modeBits >> 5) & 0x3;
+ u32 B = (modeBits >> 7) & 0x1;
params.m_Width = B + 2;
params.m_Height = A + 2;
break;
}
case 5: {
- uint32_t A = (modeBits >> 5) & 0x3;
+ u32 A = (modeBits >> 5) & 0x3;
params.m_Width = 12;
params.m_Height = A + 2;
break;
}
case 6: {
- uint32_t A = (modeBits >> 5) & 0x3;
+ u32 A = (modeBits >> 5) & 0x3;
params.m_Width = A + 2;
params.m_Height = 12;
break;
@@ -586,8 +569,8 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
}
case 9: {
- uint32_t A = (modeBits >> 5) & 0x3;
- uint32_t B = (modeBits >> 9) & 0x3;
+ u32 A = (modeBits >> 5) & 0x3;
+ u32 B = (modeBits >> 9) & 0x3;
params.m_Width = A + 6;
params.m_Height = B + 6;
break;
@@ -605,10 +588,10 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
bool H = (layout != 9) && (modeBits & 0x200);
if (H) {
- const uint32_t maxWeights[6] = {9, 11, 15, 19, 23, 31};
+ const u32 maxWeights[6] = {9, 11, 15, 19, 23, 31};
params.m_MaxWeight = maxWeights[R - 2];
} else {
- const uint32_t maxWeights[6] = {1, 2, 3, 4, 5, 7};
+ const u32 maxWeights[6] = {1, 2, 3, 4, 5, 7};
params.m_MaxWeight = maxWeights[R - 2];
}
@@ -617,32 +600,32 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
return params;
}
-static void FillVoidExtentLDR(InputBitStream& strm, uint32_t* const outBuf, uint32_t blockWidth,
- uint32_t blockHeight) {
+static void FillVoidExtentLDR(InputBitStream& strm, u32* const outBuf, u32 blockWidth,
+ u32 blockHeight) {
// Don't actually care about the void extent, just read the bits...
- for (int i = 0; i < 4; ++i) {
- strm.ReadBits(13);
+ for (s32 i = 0; i < 4; ++i) {
+ strm.ReadBits<13>();
}
// Decode the RGBA components and renormalize them to the range [0, 255]
- uint16_t r = static_cast<uint16_t>(strm.ReadBits(16));
- uint16_t g = static_cast<uint16_t>(strm.ReadBits(16));
- uint16_t b = static_cast<uint16_t>(strm.ReadBits(16));
- uint16_t a = static_cast<uint16_t>(strm.ReadBits(16));
+ u16 r = static_cast<u16>(strm.ReadBits<16>());
+ u16 g = static_cast<u16>(strm.ReadBits<16>());
+ u16 b = static_cast<u16>(strm.ReadBits<16>());
+ u16 a = static_cast<u16>(strm.ReadBits<16>());
- uint32_t rgba = (r >> 8) | (g & 0xFF00) | (static_cast<uint32_t>(b) & 0xFF00) << 8 |
- (static_cast<uint32_t>(a) & 0xFF00) << 16;
+ u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
+ (static_cast<u32>(a) & 0xFF00) << 16;
- for (uint32_t j = 0; j < blockHeight; j++) {
- for (uint32_t i = 0; i < blockWidth; i++) {
+ for (u32 j = 0; j < blockHeight; j++) {
+ for (u32 i = 0; i < blockWidth; i++) {
outBuf[j * blockWidth + i] = rgba;
}
}
}
-static void FillError(uint32_t* outBuf, uint32_t blockWidth, uint32_t blockHeight) {
- for (uint32_t j = 0; j < blockHeight; j++) {
- for (uint32_t i = 0; i < blockWidth; i++) {
+static void FillError(u32* outBuf, u32 blockWidth, u32 blockHeight) {
+ for (u32 j = 0; j < blockHeight; j++) {
+ for (u32 i = 0; i < blockWidth; i++) {
outBuf[j * blockWidth + i] = 0xFFFF00FF;
}
}
@@ -651,18 +634,18 @@ static void FillError(uint32_t* outBuf, uint32_t blockWidth, uint32_t blockHeigh
// Replicates low numBits such that [(toBit - 1):(toBit - 1 - fromBit)]
// is the same as [(numBits - 1):0] and repeats all the way down.
template <typename IntType>
-static IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) {
+static IntType Replicate(IntType val, u32 numBits, u32 toBit) {
if (numBits == 0)
return 0;
if (toBit == 0)
return 0;
IntType v = val & static_cast<IntType>((1 << numBits) - 1);
IntType res = v;
- uint32_t reslen = numBits;
+ u32 reslen = numBits;
while (reslen < toBit) {
- uint32_t comp = 0;
+ u32 comp = 0;
if (numBits > toBit - reslen) {
- uint32_t newshift = toBit - reslen;
+ u32 newshift = toBit - reslen;
comp = numBits - newshift;
numBits = newshift;
}
@@ -675,14 +658,14 @@ static IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) {
class Pixel {
protected:
- using ChannelType = int16_t;
- uint8_t m_BitDepth[4] = {8, 8, 8, 8};
- int16_t color[4] = {};
+ using ChannelType = s16;
+ u8 m_BitDepth[4] = {8, 8, 8, 8};
+ s16 color[4] = {};
public:
Pixel() = default;
- Pixel(uint32_t a, uint32_t r, uint32_t g, uint32_t b, unsigned bitDepth = 8)
- : m_BitDepth{uint8_t(bitDepth), uint8_t(bitDepth), uint8_t(bitDepth), uint8_t(bitDepth)},
+ Pixel(u32 a, u32 r, u32 g, u32 b, u32 bitDepth = 8)
+ : m_BitDepth{u8(bitDepth), u8(bitDepth), u8(bitDepth), u8(bitDepth)},
color{static_cast<ChannelType>(a), static_cast<ChannelType>(r),
static_cast<ChannelType>(g), static_cast<ChannelType>(b)} {}
@@ -691,22 +674,22 @@ public:
// significant bits when going from larger to smaller bit depth
// or by repeating the most significant bits when going from
// smaller to larger bit depths.
- void ChangeBitDepth(const uint8_t (&depth)[4]) {
- for (uint32_t i = 0; i < 4; i++) {
+ void ChangeBitDepth(const u8 (&depth)[4]) {
+ for (u32 i = 0; i < 4; i++) {
Component(i) = ChangeBitDepth(Component(i), m_BitDepth[i], depth[i]);
m_BitDepth[i] = depth[i];
}
}
template <typename IntType>
- static float ConvertChannelToFloat(IntType channel, uint8_t bitDepth) {
+ static float ConvertChannelToFloat(IntType channel, u8 bitDepth) {
float denominator = static_cast<float>((1 << bitDepth) - 1);
return static_cast<float>(channel) / denominator;
}
// Changes the bit depth of a single component. See the comment
// above for how we do this.
- static ChannelType ChangeBitDepth(Pixel::ChannelType val, uint8_t oldDepth, uint8_t newDepth) {
+ static ChannelType ChangeBitDepth(Pixel::ChannelType val, u8 oldDepth, u8 newDepth) {
assert(newDepth <= 8);
assert(oldDepth <= 8);
@@ -722,12 +705,11 @@ public:
if (newDepth == 0) {
return 0xFF;
} else {
- uint8_t bitsWasted = static_cast<uint8_t>(oldDepth - newDepth);
- uint16_t v = static_cast<uint16_t>(val);
- v = static_cast<uint16_t>((v + (1 << (bitsWasted - 1))) >> bitsWasted);
- v = ::std::min<uint16_t>(::std::max<uint16_t>(0, v),
- static_cast<uint16_t>((1 << newDepth) - 1));
- return static_cast<uint8_t>(v);
+ u8 bitsWasted = static_cast<u8>(oldDepth - newDepth);
+ u16 v = static_cast<u16>(val);
+ v = static_cast<u16>((v + (1 << (bitsWasted - 1))) >> bitsWasted);
+ v = ::std::min<u16>(::std::max<u16>(0, v), static_cast<u16>((1 << newDepth) - 1));
+ return static_cast<u8>(v);
}
}
@@ -759,15 +741,15 @@ public:
ChannelType& B() {
return color[3];
}
- const ChannelType& Component(uint32_t idx) const {
+ const ChannelType& Component(u32 idx) const {
return color[idx];
}
- ChannelType& Component(uint32_t idx) {
+ ChannelType& Component(u32 idx) {
return color[idx];
}
- void GetBitDepth(uint8_t (&outDepth)[4]) const {
- for (int i = 0; i < 4; i++) {
+ void GetBitDepth(u8 (&outDepth)[4]) const {
+ for (s32 i = 0; i < 4; i++) {
outDepth[i] = m_BitDepth[i];
}
}
@@ -776,12 +758,12 @@ public:
// and then pack each channel into an R8G8B8A8 32-bit integer. We assume
// that the architecture is little-endian, so the alpha channel will end
// up in the most-significant byte.
- uint32_t Pack() const {
+ u32 Pack() const {
Pixel eightBit(*this);
- const uint8_t eightBitDepth[4] = {8, 8, 8, 8};
+ const u8 eightBitDepth[4] = {8, 8, 8, 8};
eightBit.ChangeBitDepth(eightBitDepth);
- uint32_t r = 0;
+ u32 r = 0;
r |= eightBit.A();
r <<= 8;
r |= eightBit.B();
@@ -794,7 +776,7 @@ public:
// Clamps the pixel to the range [0,255]
void ClampByte() {
- for (uint32_t i = 0; i < 4; i++) {
+ for (u32 i = 0; i < 4; i++) {
color[i] = (color[i] < 0) ? 0 : ((color[i] > 255) ? 255 : color[i]);
}
}
@@ -804,24 +786,24 @@ public:
}
};
-static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* modes,
- const uint32_t nPartitions, const uint32_t nBitsForColorData) {
+static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nPartitions,
+ const u32 nBitsForColorData) {
// First figure out how many color values we have
- uint32_t nValues = 0;
- for (uint32_t i = 0; i < nPartitions; i++) {
+ u32 nValues = 0;
+ for (u32 i = 0; i < nPartitions; i++) {
nValues += ((modes[i] >> 2) + 1) << 1;
}
// Then based on the number of values and the remaining number of bits,
// figure out the max value for each of them...
- uint32_t range = 256;
+ u32 range = 256;
while (--range > 0) {
- IntegerEncodedValue val = IntegerEncodedValue::CreateEncoding(range);
- uint32_t bitLength = val.GetBitLength(nValues);
+ IntegerEncodedValue val = EncodingsValues[range];
+ u32 bitLength = val.GetBitLength(nValues);
if (bitLength <= nBitsForColorData) {
// Find the smallest possible range that matches the given encoding
while (--range > 0) {
- IntegerEncodedValue newval = IntegerEncodedValue::CreateEncoding(range);
+ IntegerEncodedValue newval = EncodingsValues[range];
if (!newval.MatchesEncoding(val)) {
break;
}
@@ -835,12 +817,14 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
// We now have enough to decode our integer sequence.
std::vector<IntegerEncodedValue> decodedColorValues;
+ decodedColorValues.reserve(32);
+
InputBitStream colorStream(data);
- IntegerEncodedValue::DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
+ DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
// Once we have the decoded values, we need to dequantize them to the 0-255 range
// This procedure is outlined in ASTC spec C.2.13
- uint32_t outIdx = 0;
+ u32 outIdx = 0;
for (auto itr = decodedColorValues.begin(); itr != decodedColorValues.end(); ++itr) {
// Have we already decoded all that we need?
if (outIdx >= nValues) {
@@ -848,25 +832,25 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
}
const IntegerEncodedValue& val = *itr;
- uint32_t bitlen = val.BaseBitLength();
- uint32_t bitval = val.GetBitValue();
+ u32 bitlen = val.num_bits;
+ u32 bitval = val.bit_value;
assert(bitlen >= 1);
- uint32_t A = 0, B = 0, C = 0, D = 0;
+ u32 A = 0, B = 0, C = 0, D = 0;
// A is just the lsb replicated 9 times.
A = Replicate(bitval & 1, 1, 9);
- switch (val.GetEncoding()) {
+ switch (val.encoding) {
// Replicate bits
- case eIntegerEncoding_JustBits:
+ case IntegerEncoding::JustBits:
out[outIdx++] = Replicate(bitval, bitlen, 8);
break;
// Use algorithm in C.2.13
- case eIntegerEncoding_Trit: {
+ case IntegerEncoding::Trit: {
- D = val.GetTritValue();
+ D = val.trit_value;
switch (bitlen) {
case 1: {
@@ -876,35 +860,35 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
case 2: {
C = 93;
// B = b000b0bb0
- uint32_t b = (bitval >> 1) & 1;
+ u32 b = (bitval >> 1) & 1;
B = (b << 8) | (b << 4) | (b << 2) | (b << 1);
} break;
case 3: {
C = 44;
// B = cb000cbcb
- uint32_t cb = (bitval >> 1) & 3;
+ u32 cb = (bitval >> 1) & 3;
B = (cb << 7) | (cb << 2) | cb;
} break;
case 4: {
C = 22;
// B = dcb000dcb
- uint32_t dcb = (bitval >> 1) & 7;
+ u32 dcb = (bitval >> 1) & 7;
B = (dcb << 6) | dcb;
} break;
case 5: {
C = 11;
// B = edcb000ed
- uint32_t edcb = (bitval >> 1) & 0xF;
+ u32 edcb = (bitval >> 1) & 0xF;
B = (edcb << 5) | (edcb >> 2);
} break;
case 6: {
C = 5;
// B = fedcb000f
- uint32_t fedcb = (bitval >> 1) & 0x1F;
+ u32 fedcb = (bitval >> 1) & 0x1F;
B = (fedcb << 4) | (fedcb >> 4);
} break;
@@ -912,12 +896,12 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
assert(!"Unsupported trit encoding for color values!");
break;
} // switch(bitlen)
- } // case eIntegerEncoding_Trit
+ } // case IntegerEncoding::Trit
break;
- case eIntegerEncoding_Quint: {
+ case IntegerEncoding::Qus32: {
- D = val.GetQuintValue();
+ D = val.qus32_value;
switch (bitlen) {
case 1: {
@@ -927,41 +911,41 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
case 2: {
C = 54;
// B = b0000bb00
- uint32_t b = (bitval >> 1) & 1;
+ u32 b = (bitval >> 1) & 1;
B = (b << 8) | (b << 3) | (b << 2);
} break;
case 3: {
C = 26;
// B = cb0000cbc
- uint32_t cb = (bitval >> 1) & 3;
+ u32 cb = (bitval >> 1) & 3;
B = (cb << 7) | (cb << 1) | (cb >> 1);
} break;
case 4: {
C = 13;
// B = dcb0000dc
- uint32_t dcb = (bitval >> 1) & 7;
+ u32 dcb = (bitval >> 1) & 7;
B = (dcb << 6) | (dcb >> 1);
} break;
case 5: {
C = 6;
// B = edcb0000e
- uint32_t edcb = (bitval >> 1) & 0xF;
+ u32 edcb = (bitval >> 1) & 0xF;
B = (edcb << 5) | (edcb >> 3);
} break;
default:
- assert(!"Unsupported quint encoding for color values!");
+ assert(!"Unsupported qus32 encoding for color values!");
break;
} // switch(bitlen)
- } // case eIntegerEncoding_Quint
+ } // case IntegerEncoding::Qus32
break;
- } // switch(val.GetEncoding())
+ } // switch(val.encoding)
- if (val.GetEncoding() != eIntegerEncoding_JustBits) {
- uint32_t T = D * C + B;
+ if (val.encoding != IntegerEncoding::JustBits) {
+ u32 T = D * C + B;
T ^= A;
T = (A & 0x80) | (T >> 2);
out[outIdx++] = T;
@@ -969,31 +953,31 @@ static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* mode
}
// Make sure that each of our values is in the proper range...
- for (uint32_t i = 0; i < nValues; i++) {
+ for (u32 i = 0; i < nValues; i++) {
assert(out[i] <= 255);
}
}
-static uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
- uint32_t bitval = val.GetBitValue();
- uint32_t bitlen = val.BaseBitLength();
+static u32 UnquantizeTexelWeight(const IntegerEncodedValue& val) {
+ u32 bitval = val.bit_value;
+ u32 bitlen = val.num_bits;
- uint32_t A = Replicate(bitval & 1, 1, 7);
- uint32_t B = 0, C = 0, D = 0;
+ u32 A = Replicate(bitval & 1, 1, 7);
+ u32 B = 0, C = 0, D = 0;
- uint32_t result = 0;
- switch (val.GetEncoding()) {
- case eIntegerEncoding_JustBits:
+ u32 result = 0;
+ switch (val.encoding) {
+ case IntegerEncoding::JustBits:
result = Replicate(bitval, bitlen, 6);
break;
- case eIntegerEncoding_Trit: {
- D = val.GetTritValue();
+ case IntegerEncoding::Trit: {
+ D = val.trit_value;
assert(D < 3);
switch (bitlen) {
case 0: {
- uint32_t results[3] = {0, 32, 63};
+ u32 results[3] = {0, 32, 63};
result = results[D];
} break;
@@ -1003,13 +987,13 @@ static uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
case 2: {
C = 23;
- uint32_t b = (bitval >> 1) & 1;
+ u32 b = (bitval >> 1) & 1;
B = (b << 6) | (b << 2) | b;
} break;
case 3: {
C = 11;
- uint32_t cb = (bitval >> 1) & 3;
+ u32 cb = (bitval >> 1) & 3;
B = (cb << 5) | cb;
} break;
@@ -1019,13 +1003,13 @@ static uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
}
} break;
- case eIntegerEncoding_Quint: {
- D = val.GetQuintValue();
+ case IntegerEncoding::Qus32: {
+ D = val.qus32_value;
assert(D < 5);
switch (bitlen) {
case 0: {
- uint32_t results[5] = {0, 16, 32, 47, 63};
+ u32 results[5] = {0, 16, 32, 47, 63};
result = results[D];
} break;
@@ -1035,18 +1019,18 @@ static uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
case 2: {
C = 13;
- uint32_t b = (bitval >> 1) & 1;
+ u32 b = (bitval >> 1) & 1;
B = (b << 6) | (b << 1);
} break;
default:
- assert(!"Invalid quint encoding for texel weight");
+ assert(!"Invalid qus32 encoding for texel weight");
break;
}
} break;
}
- if (val.GetEncoding() != eIntegerEncoding_JustBits && bitlen > 0) {
+ if (val.encoding != IntegerEncoding::JustBits && bitlen > 0) {
// Decode the value...
result = D * C + B;
result ^= A;
@@ -1063,12 +1047,11 @@ static uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
return result;
}
-static void UnquantizeTexelWeights(uint32_t out[2][144],
- const std::vector<IntegerEncodedValue>& weights,
- const TexelWeightParams& params, const uint32_t blockWidth,
- const uint32_t blockHeight) {
- uint32_t weightIdx = 0;
- uint32_t unquantized[2][144];
+static void UnquantizeTexelWeights(u32 out[2][144], const std::vector<IntegerEncodedValue>& weights,
+ const TexelWeightParams& params, const u32 blockWidth,
+ const u32 blockHeight) {
+ u32 weightIdx = 0;
+ u32 unquantized[2][144];
for (auto itr = weights.begin(); itr != weights.end(); ++itr) {
unquantized[0][weightIdx] = UnquantizeTexelWeight(*itr);
@@ -1086,34 +1069,34 @@ static void UnquantizeTexelWeights(uint32_t out[2][144],
}
// Do infill if necessary (Section C.2.18) ...
- uint32_t Ds = (1024 + (blockWidth / 2)) / (blockWidth - 1);
- uint32_t Dt = (1024 + (blockHeight / 2)) / (blockHeight - 1);
+ u32 Ds = (1024 + (blockWidth / 2)) / (blockWidth - 1);
+ u32 Dt = (1024 + (blockHeight / 2)) / (blockHeight - 1);
- const uint32_t kPlaneScale = params.m_bDualPlane ? 2U : 1U;
- for (uint32_t plane = 0; plane < kPlaneScale; plane++)
- for (uint32_t t = 0; t < blockHeight; t++)
- for (uint32_t s = 0; s < blockWidth; s++) {
- uint32_t cs = Ds * s;
- uint32_t ct = Dt * t;
+ const u32 kPlaneScale = params.m_bDualPlane ? 2U : 1U;
+ for (u32 plane = 0; plane < kPlaneScale; plane++)
+ for (u32 t = 0; t < blockHeight; t++)
+ for (u32 s = 0; s < blockWidth; s++) {
+ u32 cs = Ds * s;
+ u32 ct = Dt * t;
- uint32_t gs = (cs * (params.m_Width - 1) + 32) >> 6;
- uint32_t gt = (ct * (params.m_Height - 1) + 32) >> 6;
+ u32 gs = (cs * (params.m_Width - 1) + 32) >> 6;
+ u32 gt = (ct * (params.m_Height - 1) + 32) >> 6;
- uint32_t js = gs >> 4;
- uint32_t fs = gs & 0xF;
+ u32 js = gs >> 4;
+ u32 fs = gs & 0xF;
- uint32_t jt = gt >> 4;
- uint32_t ft = gt & 0x0F;
+ u32 jt = gt >> 4;
+ u32 ft = gt & 0x0F;
- uint32_t w11 = (fs * ft + 8) >> 4;
- uint32_t w10 = ft - w11;
- uint32_t w01 = fs - w11;
- uint32_t w00 = 16 - fs - ft + w11;
+ u32 w11 = (fs * ft + 8) >> 4;
+ u32 w10 = ft - w11;
+ u32 w01 = fs - w11;
+ u32 w00 = 16 - fs - ft + w11;
- uint32_t v0 = js + jt * params.m_Width;
+ u32 v0 = js + jt * params.m_Width;
#define FIND_TEXEL(tidx, bidx) \
- uint32_t p##bidx = 0; \
+ u32 p##bidx = 0; \
do { \
if ((tidx) < (params.m_Width * params.m_Height)) { \
p##bidx = unquantized[plane][(tidx)]; \
@@ -1133,7 +1116,7 @@ static void UnquantizeTexelWeights(uint32_t out[2][144],
}
// Transfers a bit as described in C.2.14
-static inline void BitTransferSigned(int32_t& a, int32_t& b) {
+static inline void BitTransferSigned(s32& a, s32& b) {
b >>= 1;
b |= a & 0x80;
a >>= 1;
@@ -1144,14 +1127,14 @@ static inline void BitTransferSigned(int32_t& a, int32_t& b) {
// Adds more precision to the blue channel as described
// in C.2.14
-static inline Pixel BlueContract(int32_t a, int32_t r, int32_t g, int32_t b) {
- return Pixel(static_cast<int16_t>(a), static_cast<int16_t>((r + b) >> 1),
- static_cast<int16_t>((g + b) >> 1), static_cast<int16_t>(b));
+static inline Pixel BlueContract(s32 a, s32 r, s32 g, s32 b) {
+ return Pixel(static_cast<s16>(a), static_cast<s16>((r + b) >> 1),
+ static_cast<s16>((g + b) >> 1), static_cast<s16>(b));
}
// Partition selection functions as specified in
// C.2.21
-static inline uint32_t hash52(uint32_t p) {
+static inline u32 hash52(u32 p) {
p ^= p >> 15;
p -= p << 17;
p += p << 7;
@@ -1165,8 +1148,7 @@ static inline uint32_t hash52(uint32_t p) {
return p;
}
-static uint32_t SelectPartition(int32_t seed, int32_t x, int32_t y, int32_t z,
- int32_t partitionCount, int32_t smallBlock) {
+static u32 SelectPartition(s32 seed, s32 x, s32 y, s32 z, s32 partitionCount, s32 smallBlock) {
if (1 == partitionCount)
return 0;
@@ -1178,34 +1160,34 @@ static uint32_t SelectPartition(int32_t seed, int32_t x, int32_t y, int32_t z,
seed += (partitionCount - 1) * 1024;
- uint32_t rnum = hash52(static_cast<uint32_t>(seed));
- uint8_t seed1 = static_cast<uint8_t>(rnum & 0xF);
- uint8_t seed2 = static_cast<uint8_t>((rnum >> 4) & 0xF);
- uint8_t seed3 = static_cast<uint8_t>((rnum >> 8) & 0xF);
- uint8_t seed4 = static_cast<uint8_t>((rnum >> 12) & 0xF);
- uint8_t seed5 = static_cast<uint8_t>((rnum >> 16) & 0xF);
- uint8_t seed6 = static_cast<uint8_t>((rnum >> 20) & 0xF);
- uint8_t seed7 = static_cast<uint8_t>((rnum >> 24) & 0xF);
- uint8_t seed8 = static_cast<uint8_t>((rnum >> 28) & 0xF);
- uint8_t seed9 = static_cast<uint8_t>((rnum >> 18) & 0xF);
- uint8_t seed10 = static_cast<uint8_t>((rnum >> 22) & 0xF);
- uint8_t seed11 = static_cast<uint8_t>((rnum >> 26) & 0xF);
- uint8_t seed12 = static_cast<uint8_t>(((rnum >> 30) | (rnum << 2)) & 0xF);
-
- seed1 = static_cast<uint8_t>(seed1 * seed1);
- seed2 = static_cast<uint8_t>(seed2 * seed2);
- seed3 = static_cast<uint8_t>(seed3 * seed3);
- seed4 = static_cast<uint8_t>(seed4 * seed4);
- seed5 = static_cast<uint8_t>(seed5 * seed5);
- seed6 = static_cast<uint8_t>(seed6 * seed6);
- seed7 = static_cast<uint8_t>(seed7 * seed7);
- seed8 = static_cast<uint8_t>(seed8 * seed8);
- seed9 = static_cast<uint8_t>(seed9 * seed9);
- seed10 = static_cast<uint8_t>(seed10 * seed10);
- seed11 = static_cast<uint8_t>(seed11 * seed11);
- seed12 = static_cast<uint8_t>(seed12 * seed12);
-
- int32_t sh1, sh2, sh3;
+ u32 rnum = hash52(static_cast<u32>(seed));
+ u8 seed1 = static_cast<u8>(rnum & 0xF);
+ u8 seed2 = static_cast<u8>((rnum >> 4) & 0xF);
+ u8 seed3 = static_cast<u8>((rnum >> 8) & 0xF);
+ u8 seed4 = static_cast<u8>((rnum >> 12) & 0xF);
+ u8 seed5 = static_cast<u8>((rnum >> 16) & 0xF);
+ u8 seed6 = static_cast<u8>((rnum >> 20) & 0xF);
+ u8 seed7 = static_cast<u8>((rnum >> 24) & 0xF);
+ u8 seed8 = static_cast<u8>((rnum >> 28) & 0xF);
+ u8 seed9 = static_cast<u8>((rnum >> 18) & 0xF);
+ u8 seed10 = static_cast<u8>((rnum >> 22) & 0xF);
+ u8 seed11 = static_cast<u8>((rnum >> 26) & 0xF);
+ u8 seed12 = static_cast<u8>(((rnum >> 30) | (rnum << 2)) & 0xF);
+
+ seed1 = static_cast<u8>(seed1 * seed1);
+ seed2 = static_cast<u8>(seed2 * seed2);
+ seed3 = static_cast<u8>(seed3 * seed3);
+ seed4 = static_cast<u8>(seed4 * seed4);
+ seed5 = static_cast<u8>(seed5 * seed5);
+ seed6 = static_cast<u8>(seed6 * seed6);
+ seed7 = static_cast<u8>(seed7 * seed7);
+ seed8 = static_cast<u8>(seed8 * seed8);
+ seed9 = static_cast<u8>(seed9 * seed9);
+ seed10 = static_cast<u8>(seed10 * seed10);
+ seed11 = static_cast<u8>(seed11 * seed11);
+ seed12 = static_cast<u8>(seed12 * seed12);
+
+ s32 sh1, sh2, sh3;
if (seed & 1) {
sh1 = (seed & 2) ? 4 : 5;
sh2 = (partitionCount == 3) ? 6 : 5;
@@ -1215,23 +1197,23 @@ static uint32_t SelectPartition(int32_t seed, int32_t x, int32_t y, int32_t z,
}
sh3 = (seed & 0x10) ? sh1 : sh2;
- seed1 = static_cast<uint8_t>(seed1 >> sh1);
- seed2 = static_cast<uint8_t>(seed2 >> sh2);
- seed3 = static_cast<uint8_t>(seed3 >> sh1);
- seed4 = static_cast<uint8_t>(seed4 >> sh2);
- seed5 = static_cast<uint8_t>(seed5 >> sh1);
- seed6 = static_cast<uint8_t>(seed6 >> sh2);
- seed7 = static_cast<uint8_t>(seed7 >> sh1);
- seed8 = static_cast<uint8_t>(seed8 >> sh2);
- seed9 = static_cast<uint8_t>(seed9 >> sh3);
- seed10 = static_cast<uint8_t>(seed10 >> sh3);
- seed11 = static_cast<uint8_t>(seed11 >> sh3);
- seed12 = static_cast<uint8_t>(seed12 >> sh3);
-
- int32_t a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
- int32_t b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
- int32_t c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
- int32_t d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
+ seed1 = static_cast<u8>(seed1 >> sh1);
+ seed2 = static_cast<u8>(seed2 >> sh2);
+ seed3 = static_cast<u8>(seed3 >> sh1);
+ seed4 = static_cast<u8>(seed4 >> sh2);
+ seed5 = static_cast<u8>(seed5 >> sh1);
+ seed6 = static_cast<u8>(seed6 >> sh2);
+ seed7 = static_cast<u8>(seed7 >> sh1);
+ seed8 = static_cast<u8>(seed8 >> sh2);
+ seed9 = static_cast<u8>(seed9 >> sh3);
+ seed10 = static_cast<u8>(seed10 >> sh3);
+ seed11 = static_cast<u8>(seed11 >> sh3);
+ seed12 = static_cast<u8>(seed12 >> sh3);
+
+ s32 a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
+ s32 b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
+ s32 c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
+ s32 d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
a &= 0x3F;
b &= 0x3F;
@@ -1252,27 +1234,26 @@ static uint32_t SelectPartition(int32_t seed, int32_t x, int32_t y, int32_t z,
return 3;
}
-static inline uint32_t Select2DPartition(int32_t seed, int32_t x, int32_t y, int32_t partitionCount,
- int32_t smallBlock) {
+static inline u32 Select2DPartition(s32 seed, s32 x, s32 y, s32 partitionCount, s32 smallBlock) {
return SelectPartition(seed, x, y, 0, partitionCount, smallBlock);
}
// Section C.2.14
-static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValues,
- uint32_t colorEndpointMode) {
+static void ComputeEndpos32s(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
+ u32 colorEndpos32Mode) {
#define READ_UINT_VALUES(N) \
- uint32_t v[N]; \
- for (uint32_t i = 0; i < N; i++) { \
+ u32 v[N]; \
+ for (u32 i = 0; i < N; i++) { \
v[i] = *(colorValues++); \
}
#define READ_INT_VALUES(N) \
- int32_t v[N]; \
- for (uint32_t i = 0; i < N; i++) { \
- v[i] = static_cast<int32_t>(*(colorValues++)); \
+ s32 v[N]; \
+ for (u32 i = 0; i < N; i++) { \
+ v[i] = static_cast<s32>(*(colorValues++)); \
}
- switch (colorEndpointMode) {
+ switch (colorEndpos32Mode) {
case 0: {
READ_UINT_VALUES(2)
ep1 = Pixel(0xFF, v[0], v[0], v[0]);
@@ -1281,8 +1262,8 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValue
case 1: {
READ_UINT_VALUES(2)
- uint32_t L0 = (v[0] >> 2) | (v[1] & 0xC0);
- uint32_t L1 = std::max(L0 + (v[1] & 0x3F), 0xFFU);
+ u32 L0 = (v[0] >> 2) | (v[1] & 0xC0);
+ u32 L1 = std::max(L0 + (v[1] & 0x3F), 0xFFU);
ep1 = Pixel(0xFF, L0, L0, L0);
ep2 = Pixel(0xFF, L1, L1, L1);
} break;
@@ -1371,7 +1352,7 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValue
} break;
default:
- assert(!"Unsupported color endpoint mode (is it HDR?)");
+ assert(!"Unsupported color endpos32 mode (is it HDR?)");
break;
}
@@ -1379,8 +1360,8 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValue
#undef READ_INT_VALUES
}
-static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
- const uint32_t blockHeight, uint32_t* outBuf) {
+static void DecompressBlock(const u8 inBuf[16], const u32 blockWidth, const u32 blockHeight,
+ u32* outBuf) {
InputBitStream strm(inBuf);
TexelWeightParams weightParams = DecodeBlockInfo(strm);
@@ -1415,7 +1396,7 @@ static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
}
// Read num partitions
- uint32_t nPartitions = strm.ReadBits(2) + 1;
+ u32 nPartitions = strm.ReadBits<2>() + 1;
assert(nPartitions <= 4);
if (nPartitions == 4 && weightParams.m_bDualPlane) {
@@ -1424,36 +1405,36 @@ static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
return;
}
- // Based on the number of partitions, read the color endpoint mode for
+ // Based on the number of partitions, read the color endpos32 mode for
// each partition.
- // Determine partitions, partition index, and color endpoint modes
- int32_t planeIdx = -1;
- uint32_t partitionIndex;
- uint32_t colorEndpointMode[4] = {0, 0, 0, 0};
+ // Determine partitions, partition index, and color endpos32 modes
+ s32 planeIdx = -1;
+ u32 partitionIndex;
+ u32 colorEndpos32Mode[4] = {0, 0, 0, 0};
// Define color data.
- uint8_t colorEndpointData[16];
- memset(colorEndpointData, 0, sizeof(colorEndpointData));
- OutputBitStream colorEndpointStream(colorEndpointData, 16 * 8, 0);
+ u8 colorEndpos32Data[16];
+ memset(colorEndpos32Data, 0, sizeof(colorEndpos32Data));
+ OutputBitStream colorEndpos32Stream(colorEndpos32Data, 16 * 8, 0);
// Read extra config data...
- uint32_t baseCEM = 0;
+ u32 baseCEM = 0;
if (nPartitions == 1) {
- colorEndpointMode[0] = strm.ReadBits(4);
+ colorEndpos32Mode[0] = strm.ReadBits<4>();
partitionIndex = 0;
} else {
- partitionIndex = strm.ReadBits(10);
- baseCEM = strm.ReadBits(6);
+ partitionIndex = strm.ReadBits<10>();
+ baseCEM = strm.ReadBits<6>();
}
- uint32_t baseMode = (baseCEM & 3);
+ u32 baseMode = (baseCEM & 3);
- // Remaining bits are color endpoint data...
- uint32_t nWeightBits = weightParams.GetPackedBitSize();
- int32_t remainingBits = 128 - nWeightBits - strm.GetBitsRead();
+ // Remaining bits are color endpos32 data...
+ u32 nWeightBits = weightParams.GetPackedBitSize();
+ s32 remainingBits = 128 - nWeightBits - static_cast<s32>(strm.GetBitsRead());
// Consider extra bits prior to texel data...
- uint32_t extraCEMbits = 0;
+ u32 extraCEMbits = 0;
if (baseMode) {
switch (nPartitions) {
case 2:
@@ -1473,18 +1454,18 @@ static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
remainingBits -= extraCEMbits;
// Do we have a dual plane situation?
- uint32_t planeSelectorBits = 0;
+ u32 planeSelectorBits = 0;
if (weightParams.m_bDualPlane) {
planeSelectorBits = 2;
}
remainingBits -= planeSelectorBits;
// Read color data...
- uint32_t colorDataBits = remainingBits;
+ u32 colorDataBits = remainingBits;
while (remainingBits > 0) {
- uint32_t nb = std::min(remainingBits, 8);
- uint32_t b = strm.ReadBits(nb);
- colorEndpointStream.WriteBits(b, nb);
+ u32 nb = std::min(remainingBits, 8);
+ u32 b = strm.ReadBits(nb);
+ colorEndpos32Stream.WriteBits(b, nb);
remainingBits -= 8;
}
@@ -1493,64 +1474,64 @@ static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
// Read the rest of the CEM
if (baseMode) {
- uint32_t extraCEM = strm.ReadBits(extraCEMbits);
- uint32_t CEM = (extraCEM << 6) | baseCEM;
+ u32 extraCEM = strm.ReadBits(extraCEMbits);
+ u32 CEM = (extraCEM << 6) | baseCEM;
CEM >>= 2;
bool C[4] = {0};
- for (uint32_t i = 0; i < nPartitions; i++) {
+ for (u32 i = 0; i < nPartitions; i++) {
C[i] = CEM & 1;
CEM >>= 1;
}
- uint8_t M[4] = {0};
- for (uint32_t i = 0; i < nPartitions; i++) {
+ u8 M[4] = {0};
+ for (u32 i = 0; i < nPartitions; i++) {
M[i] = CEM & 3;
CEM >>= 2;
assert(M[i] <= 3);
}
- for (uint32_t i = 0; i < nPartitions; i++) {
- colorEndpointMode[i] = baseMode;
+ for (u32 i = 0; i < nPartitions; i++) {
+ colorEndpos32Mode[i] = baseMode;
if (!(C[i]))
- colorEndpointMode[i] -= 1;
- colorEndpointMode[i] <<= 2;
- colorEndpointMode[i] |= M[i];
+ colorEndpos32Mode[i] -= 1;
+ colorEndpos32Mode[i] <<= 2;
+ colorEndpos32Mode[i] |= M[i];
}
} else if (nPartitions > 1) {
- uint32_t CEM = baseCEM >> 2;
- for (uint32_t i = 0; i < nPartitions; i++) {
- colorEndpointMode[i] = CEM;
+ u32 CEM = baseCEM >> 2;
+ for (u32 i = 0; i < nPartitions; i++) {
+ colorEndpos32Mode[i] = CEM;
}
}
// Make sure everything up till here is sane.
- for (uint32_t i = 0; i < nPartitions; i++) {
- assert(colorEndpointMode[i] < 16);
+ for (u32 i = 0; i < nPartitions; i++) {
+ assert(colorEndpos32Mode[i] < 16);
}
assert(strm.GetBitsRead() + weightParams.GetPackedBitSize() == 128);
// Decode both color data and texel weight data
- uint32_t colorValues[32]; // Four values, two endpoints, four maximum paritions
- DecodeColorValues(colorValues, colorEndpointData, colorEndpointMode, nPartitions,
+ u32 colorValues[32]; // Four values, two endpos32s, four maximum paritions
+ DecodeColorValues(colorValues, colorEndpos32Data, colorEndpos32Mode, nPartitions,
colorDataBits);
- Pixel endpoints[4][2];
- const uint32_t* colorValuesPtr = colorValues;
- for (uint32_t i = 0; i < nPartitions; i++) {
- ComputeEndpoints(endpoints[i][0], endpoints[i][1], colorValuesPtr, colorEndpointMode[i]);
+ Pixel endpos32s[4][2];
+ const u32* colorValuesPtr = colorValues;
+ for (u32 i = 0; i < nPartitions; i++) {
+ ComputeEndpos32s(endpos32s[i][0], endpos32s[i][1], colorValuesPtr, colorEndpos32Mode[i]);
}
// Read the texel weight data..
- uint8_t texelWeightData[16];
+ u8 texelWeightData[16];
memcpy(texelWeightData, inBuf, sizeof(texelWeightData));
// Reverse everything
- for (uint32_t i = 0; i < 8; i++) {
+ for (u32 i = 0; i < 8; i++) {
// Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
#define REVERSE_BYTE(b) (((b)*0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32
- unsigned char a = static_cast<unsigned char>(REVERSE_BYTE(texelWeightData[i]));
- unsigned char b = static_cast<unsigned char>(REVERSE_BYTE(texelWeightData[15 - i]));
+ u8 a = static_cast<u8>(REVERSE_BYTE(texelWeightData[i]));
+ u8 b = static_cast<u8>(REVERSE_BYTE(texelWeightData[15 - i]));
#undef REVERSE_BYTE
texelWeightData[i] = b;
@@ -1558,50 +1539,51 @@ static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
}
// Make sure that higher non-texel bits are set to zero
- const uint32_t clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1;
+ const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1;
texelWeightData[clearByteStart - 1] =
texelWeightData[clearByteStart - 1] &
- static_cast<uint8_t>((1 << (weightParams.GetPackedBitSize() % 8)) - 1);
+ static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1);
memset(texelWeightData + clearByteStart, 0, 16 - clearByteStart);
std::vector<IntegerEncodedValue> texelWeightValues;
+ texelWeightValues.reserve(64);
+
InputBitStream weightStream(texelWeightData);
- IntegerEncodedValue::DecodeIntegerSequence(texelWeightValues, weightStream,
- weightParams.m_MaxWeight,
- weightParams.GetNumWeightValues());
+ DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight,
+ weightParams.GetNumWeightValues());
// Blocks can be at most 12x12, so we can have as many as 144 weights
- uint32_t weights[2][144];
+ u32 weights[2][144];
UnquantizeTexelWeights(weights, texelWeightValues, weightParams, blockWidth, blockHeight);
- // Now that we have endpoints and weights, we can interpolate and generate
+ // Now that we have endpos32s and weights, we can s32erpolate and generate
// the proper decoding...
- for (uint32_t j = 0; j < blockHeight; j++)
- for (uint32_t i = 0; i < blockWidth; i++) {
- uint32_t partition = Select2DPartition(partitionIndex, i, j, nPartitions,
- (blockHeight * blockWidth) < 32);
+ for (u32 j = 0; j < blockHeight; j++)
+ for (u32 i = 0; i < blockWidth; i++) {
+ u32 partition = Select2DPartition(partitionIndex, i, j, nPartitions,
+ (blockHeight * blockWidth) < 32);
assert(partition < nPartitions);
Pixel p;
- for (uint32_t c = 0; c < 4; c++) {
- uint32_t C0 = endpoints[partition][0].Component(c);
+ for (u32 c = 0; c < 4; c++) {
+ u32 C0 = endpos32s[partition][0].Component(c);
C0 = Replicate(C0, 8, 16);
- uint32_t C1 = endpoints[partition][1].Component(c);
+ u32 C1 = endpos32s[partition][1].Component(c);
C1 = Replicate(C1, 8, 16);
- uint32_t plane = 0;
+ u32 plane = 0;
if (weightParams.m_bDualPlane && (((planeIdx + 1) & 3) == c)) {
plane = 1;
}
- uint32_t weight = weights[plane][j * blockWidth + i];
- uint32_t C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
+ u32 weight = weights[plane][j * blockWidth + i];
+ u32 C = (C0 * (64 - weight) + C1 * weight + 32) / 64;
if (C == 65535) {
p.Component(c) = 255;
} else {
double Cf = static_cast<double>(C);
- p.Component(c) = static_cast<uint16_t>(255.0 * (Cf / 65536.0) + 0.5);
+ p.Component(c) = static_cast<u16>(255.0 * (Cf / 65536.0) + 0.5);
}
}
@@ -1613,26 +1595,26 @@ static void DecompressBlock(const uint8_t inBuf[16], const uint32_t blockWidth,
namespace Tegra::Texture::ASTC {
-std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height,
- uint32_t depth, uint32_t block_width, uint32_t block_height) {
- uint32_t blockIdx = 0;
+std::vector<u8> Decompress(const u8* data, u32 width, u32 height, u32 depth, u32 block_width,
+ u32 block_height) {
+ u32 blockIdx = 0;
std::size_t depth_offset = 0;
- std::vector<uint8_t> outData(height * width * depth * 4);
- for (uint32_t k = 0; k < depth; k++) {
- for (uint32_t j = 0; j < height; j += block_height) {
- for (uint32_t i = 0; i < width; i += block_width) {
+ std::vector<u8> outData(height * width * depth * 4);
+ for (u32 k = 0; k < depth; k++) {
+ for (u32 j = 0; j < height; j += block_height) {
+ for (u32 i = 0; i < width; i += block_width) {
- const uint8_t* blockPtr = data + blockIdx * 16;
+ const u8* blockPtr = data + blockIdx * 16;
// Blocks can be at most 12x12
- uint32_t uncompData[144];
+ u32 uncompData[144];
ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
- uint32_t decompWidth = std::min(block_width, width - i);
- uint32_t decompHeight = std::min(block_height, height - j);
+ u32 decompWidth = std::min(block_width, width - i);
+ u32 decompHeight = std::min(block_height, height - j);
- uint8_t* outRow = depth_offset + outData.data() + (j * width + i) * 4;
- for (uint32_t jj = 0; jj < decompHeight; jj++) {
+ u8* outRow = depth_offset + outData.data() + (j * width + i) * 4;
+ for (u32 jj = 0; jj < decompHeight; jj++) {
memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
}