shader/texture: Deduce texture buffers from locker
Instead of specializing shaders to separate texture buffers from 1D textures, use the locker to deduce them while they are being decoded.pull/3098/head
parent
c52f37f259
commit
32c1bc6a67
|
|
@ -271,9 +271,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||||
const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
|
const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
|
||||||
SetupDrawConstBuffers(stage, shader);
|
SetupDrawConstBuffers(stage, shader);
|
||||||
SetupDrawGlobalMemory(stage, shader);
|
SetupDrawGlobalMemory(stage, shader);
|
||||||
const auto texture_buffer_usage{SetupDrawTextures(stage, shader, base_bindings)};
|
SetupDrawTextures(stage, shader, base_bindings);
|
||||||
|
|
||||||
const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
|
const ProgramVariant variant{base_bindings, primitive_mode};
|
||||||
const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
|
const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
|
||||||
|
|
||||||
switch (program) {
|
switch (program) {
|
||||||
|
|
@ -303,7 +303,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||||
// When VertexA is enabled, we have dual vertex shaders
|
// When VertexA is enabled, we have dual vertex shaders
|
||||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||||
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
||||||
index++;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
base_bindings = next_bindings;
|
base_bindings = next_bindings;
|
||||||
|
|
@ -732,11 +732,10 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto kernel = shader_cache.GetComputeKernel(code_addr);
|
auto kernel = shader_cache.GetComputeKernel(code_addr);
|
||||||
ProgramVariant variant;
|
SetupComputeTextures(kernel);
|
||||||
variant.texture_buffer_usage = SetupComputeTextures(kernel);
|
|
||||||
SetupComputeImages(kernel);
|
SetupComputeImages(kernel);
|
||||||
|
|
||||||
const auto [program, next_bindings] = kernel->GetProgramHandle(variant);
|
const auto [program, next_bindings] = kernel->GetProgramHandle({});
|
||||||
state.draw.shader_program = program;
|
state.draw.shader_program = program;
|
||||||
state.draw.program_pipeline = 0;
|
state.draw.program_pipeline = 0;
|
||||||
|
|
||||||
|
|
@ -918,9 +917,8 @@ void RasterizerOpenGL::SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entr
|
||||||
bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
|
bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage,
|
void RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage, const Shader& shader,
|
||||||
const Shader& shader,
|
BaseBindings base_bindings) {
|
||||||
BaseBindings base_bindings) {
|
|
||||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||||
const auto& gpu = system.GPU();
|
const auto& gpu = system.GPU();
|
||||||
const auto& maxwell3d = gpu.Maxwell3D();
|
const auto& maxwell3d = gpu.Maxwell3D();
|
||||||
|
|
@ -929,8 +927,6 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
|
||||||
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures),
|
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures),
|
||||||
"Exceeded the number of active textures.");
|
"Exceeded the number of active textures.");
|
||||||
|
|
||||||
TextureBufferUsage texture_buffer_usage{0};
|
|
||||||
|
|
||||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||||
const auto& entry = entries[bindpoint];
|
const auto& entry = entries[bindpoint];
|
||||||
const auto texture = [&] {
|
const auto texture = [&] {
|
||||||
|
|
@ -943,15 +939,11 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
|
||||||
return maxwell3d.GetTextureInfo(tex_handle);
|
return maxwell3d.GetTextureInfo(tex_handle);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (SetupTexture(base_bindings.sampler + bindpoint, texture, entry)) {
|
SetupTexture(base_bindings.sampler + bindpoint, texture, entry);
|
||||||
texture_buffer_usage.set(bindpoint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture_buffer_usage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
|
void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
|
||||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||||
const auto& compute = system.GPU().KeplerCompute();
|
const auto& compute = system.GPU().KeplerCompute();
|
||||||
const auto& entries = kernel->GetShaderEntries().samplers;
|
const auto& entries = kernel->GetShaderEntries().samplers;
|
||||||
|
|
@ -959,8 +951,6 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
|
||||||
ASSERT_MSG(entries.size() <= std::size(state.textures),
|
ASSERT_MSG(entries.size() <= std::size(state.textures),
|
||||||
"Exceeded the number of active textures.");
|
"Exceeded the number of active textures.");
|
||||||
|
|
||||||
TextureBufferUsage texture_buffer_usage{0};
|
|
||||||
|
|
||||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||||
const auto& entry = entries[bindpoint];
|
const auto& entry = entries[bindpoint];
|
||||||
const auto texture = [&] {
|
const auto texture = [&] {
|
||||||
|
|
@ -972,34 +962,29 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
|
||||||
return compute.GetTextureInfo(tex_handle);
|
return compute.GetTextureInfo(tex_handle);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (SetupTexture(bindpoint, texture, entry)) {
|
SetupTexture(bindpoint, texture, entry);
|
||||||
texture_buffer_usage.set(bindpoint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture_buffer_usage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
||||||
const GLShader::SamplerEntry& entry) {
|
const GLShader::SamplerEntry& entry) {
|
||||||
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
|
|
||||||
|
|
||||||
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
|
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
|
||||||
if (!view) {
|
if (!view) {
|
||||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||||
|
state.samplers[binding] = 0;
|
||||||
state.textures[binding] = 0;
|
state.textures[binding] = 0;
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
state.textures[binding] = view->GetTexture();
|
state.textures[binding] = view->GetTexture();
|
||||||
|
|
||||||
if (view->GetSurfaceParams().IsBuffer()) {
|
if (view->GetSurfaceParams().IsBuffer()) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
|
||||||
|
|
||||||
// Apply swizzle to textures that are not buffers.
|
// Apply swizzle to textures that are not buffers.
|
||||||
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
|
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
|
||||||
texture.tic.w_source);
|
texture.tic.w_source);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
|
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
|
||||||
|
|
|
||||||
|
|
@ -107,16 +107,15 @@ private:
|
||||||
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
|
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
|
||||||
void DrawPrelude();
|
void DrawPrelude();
|
||||||
|
|
||||||
/// Configures the current textures to use for the draw command. Returns shaders texture buffer
|
/// Configures the current textures to use for the draw command.
|
||||||
/// usage.
|
void SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||||
TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
BaseBindings base_bindings);
|
||||||
const Shader& shader, BaseBindings base_bindings);
|
|
||||||
|
|
||||||
/// Configures the textures used in a compute shader. Returns texture buffer usage.
|
/// Configures the textures used in a compute shader.
|
||||||
TextureBufferUsage SetupComputeTextures(const Shader& kernel);
|
void SetupComputeTextures(const Shader& kernel);
|
||||||
|
|
||||||
/// Configures a texture. Returns true when the texture is a texture buffer.
|
/// Configures a texture.
|
||||||
bool SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
||||||
const GLShader::SamplerEntry& entry);
|
const GLShader::SamplerEntry& entry);
|
||||||
|
|
||||||
/// Configures images in a compute shader.
|
/// Configures images in a compute shader.
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,6 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramTy
|
||||||
|
|
||||||
auto base_bindings{variant.base_bindings};
|
auto base_bindings{variant.base_bindings};
|
||||||
const auto primitive_mode{variant.primitive_mode};
|
const auto primitive_mode{variant.primitive_mode};
|
||||||
const auto texture_buffer_usage{variant.texture_buffer_usage};
|
|
||||||
|
|
||||||
std::string source = fmt::format(R"(// {}
|
std::string source = fmt::format(R"(// {}
|
||||||
#version 430 core
|
#version 430 core
|
||||||
|
|
@ -317,17 +316,6 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramTy
|
||||||
fmt::format("#define IMAGE_BINDING_{} {}\n", image.GetIndex(), base_bindings.image++);
|
fmt::format("#define IMAGE_BINDING_{} {}\n", image.GetIndex(), base_bindings.image++);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform 1D textures to texture samplers by declaring its preprocessor macros.
|
|
||||||
for (std::size_t i = 0; i < texture_buffer_usage.size(); ++i) {
|
|
||||||
if (!texture_buffer_usage.test(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
source += fmt::format("#define SAMPLER_{}_IS_BUFFER\n", i);
|
|
||||||
}
|
|
||||||
if (texture_buffer_usage.any()) {
|
|
||||||
source += '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program_type == ProgramType::Geometry) {
|
if (program_type == ProgramType::Geometry) {
|
||||||
const auto [glsl_topology, debug_name, max_vertices] =
|
const auto [glsl_topology, debug_name, max_vertices] =
|
||||||
GetPrimitiveDescription(primitive_mode);
|
GetPrimitiveDescription(primitive_mode);
|
||||||
|
|
|
||||||
|
|
@ -658,9 +658,11 @@ private:
|
||||||
const std::string description{"layout (binding = SAMPLER_BINDING_" +
|
const std::string description{"layout (binding = SAMPLER_BINDING_" +
|
||||||
std::to_string(sampler.GetIndex()) + ") uniform"};
|
std::to_string(sampler.GetIndex()) + ") uniform"};
|
||||||
std::string sampler_type = [&]() {
|
std::string sampler_type = [&]() {
|
||||||
|
if (sampler.IsBuffer()) {
|
||||||
|
return "samplerBuffer";
|
||||||
|
}
|
||||||
switch (sampler.GetType()) {
|
switch (sampler.GetType()) {
|
||||||
case Tegra::Shader::TextureType::Texture1D:
|
case Tegra::Shader::TextureType::Texture1D:
|
||||||
// Special cased, read below.
|
|
||||||
return "sampler1D";
|
return "sampler1D";
|
||||||
case Tegra::Shader::TextureType::Texture2D:
|
case Tegra::Shader::TextureType::Texture2D:
|
||||||
return "sampler2D";
|
return "sampler2D";
|
||||||
|
|
@ -680,19 +682,7 @@ private:
|
||||||
sampler_type += "Shadow";
|
sampler_type += "Shadow";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampler.GetType() == Tegra::Shader::TextureType::Texture1D) {
|
code.AddLine("{} {} {};", description, sampler_type, name);
|
||||||
// 1D textures can be aliased to texture buffers, hide the declarations behind a
|
|
||||||
// preprocessor flag and use one or the other from the GPU state. This has to be
|
|
||||||
// done because shaders don't have enough information to determine the texture type.
|
|
||||||
EmitIfdefIsBuffer(sampler);
|
|
||||||
code.AddLine("{} samplerBuffer {};", description, name);
|
|
||||||
code.AddLine("#else");
|
|
||||||
code.AddLine("{} {} {};", description, sampler_type, name);
|
|
||||||
code.AddLine("#endif");
|
|
||||||
} else {
|
|
||||||
// The other texture types (2D, 3D and cubes) don't have this issue.
|
|
||||||
code.AddLine("{} {} {};", description, sampler_type, name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!samplers.empty()) {
|
if (!samplers.empty()) {
|
||||||
code.AddNewLine();
|
code.AddNewLine();
|
||||||
|
|
@ -1749,27 +1739,14 @@ private:
|
||||||
expr += ", ";
|
expr += ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store a copy of the expression without the lod to be used with texture buffers
|
if (meta->lod && !meta->sampler.IsBuffer()) {
|
||||||
std::string expr_buffer = expr;
|
|
||||||
|
|
||||||
if (meta->lod) {
|
|
||||||
expr += ", ";
|
expr += ", ";
|
||||||
expr += Visit(meta->lod).AsInt();
|
expr += Visit(meta->lod).AsInt();
|
||||||
}
|
}
|
||||||
expr += ')';
|
expr += ')';
|
||||||
expr += GetSwizzle(meta->element);
|
expr += GetSwizzle(meta->element);
|
||||||
|
|
||||||
expr_buffer += ')';
|
return {std::move(expr), Type::Float};
|
||||||
expr_buffer += GetSwizzle(meta->element);
|
|
||||||
|
|
||||||
const std::string tmp{code.GenerateTemporary()};
|
|
||||||
EmitIfdefIsBuffer(meta->sampler);
|
|
||||||
code.AddLine("float {} = {};", tmp, expr_buffer);
|
|
||||||
code.AddLine("#else");
|
|
||||||
code.AddLine("float {} = {};", tmp, expr);
|
|
||||||
code.AddLine("#endif");
|
|
||||||
|
|
||||||
return {tmp, Type::Float};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression ImageLoad(Operation operation) {
|
Expression ImageLoad(Operation operation) {
|
||||||
|
|
@ -2214,10 +2191,6 @@ private:
|
||||||
return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
|
return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitIfdefIsBuffer(const Sampler& sampler) {
|
|
||||||
code.AddLine("#ifdef SAMPLER_{}_IS_BUFFER", sampler.GetIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
|
std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
|
||||||
return fmt::format("{}_{}_{}", name, index, suffix);
|
return fmt::format("{}_{}_{}", name, index, suffix);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,6 @@ using VideoCommon::Shader::KeyMap;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct ConstBufferKey {
|
|
||||||
u32 cbuf;
|
|
||||||
u32 offset;
|
|
||||||
u32 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BoundSamplerKey {
|
|
||||||
u32 offset;
|
|
||||||
Tegra::Engines::SamplerDescriptor sampler;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BindlessSamplerKey {
|
|
||||||
u32 cbuf;
|
|
||||||
u32 offset;
|
|
||||||
Tegra::Engines::SamplerDescriptor sampler;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ShaderCacheVersionHash = std::array<u8, 64>;
|
using ShaderCacheVersionHash = std::array<u8, 64>;
|
||||||
|
|
||||||
enum class TransferableEntryKind : u32 {
|
enum class TransferableEntryKind : u32 {
|
||||||
|
|
@ -52,10 +35,28 @@ enum class TransferableEntryKind : u32 {
|
||||||
Usage,
|
Usage,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 NativeVersion = 5;
|
struct ConstBufferKey {
|
||||||
|
u32 cbuf{};
|
||||||
|
u32 offset{};
|
||||||
|
u32 value{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundSamplerKey {
|
||||||
|
u32 offset{};
|
||||||
|
Tegra::Engines::SamplerDescriptor sampler{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BindlessSamplerKey {
|
||||||
|
u32 cbuf{};
|
||||||
|
u32 offset{};
|
||||||
|
Tegra::Engines::SamplerDescriptor sampler{};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u32 NativeVersion = 6;
|
||||||
|
|
||||||
// Making sure sizes doesn't change by accident
|
// Making sure sizes doesn't change by accident
|
||||||
static_assert(sizeof(BaseBindings) == 16);
|
static_assert(sizeof(BaseBindings) == 16);
|
||||||
|
static_assert(sizeof(ProgramVariant) == 20);
|
||||||
|
|
||||||
ShaderCacheVersionHash GetShaderCacheVersionHash() {
|
ShaderCacheVersionHash GetShaderCacheVersionHash() {
|
||||||
ShaderCacheVersionHash hash{};
|
ShaderCacheVersionHash hash{};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <bitset>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
@ -37,7 +36,6 @@ struct ShaderDiskCacheDump;
|
||||||
|
|
||||||
using ProgramCode = std::vector<u64>;
|
using ProgramCode = std::vector<u64>;
|
||||||
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
|
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
|
||||||
using TextureBufferUsage = std::bitset<64>;
|
|
||||||
|
|
||||||
/// Allocated bindings used by an OpenGL shader program
|
/// Allocated bindings used by an OpenGL shader program
|
||||||
struct BaseBindings {
|
struct BaseBindings {
|
||||||
|
|
@ -61,11 +59,10 @@ static_assert(std::is_trivially_copyable_v<BaseBindings>);
|
||||||
struct ProgramVariant {
|
struct ProgramVariant {
|
||||||
BaseBindings base_bindings;
|
BaseBindings base_bindings;
|
||||||
GLenum primitive_mode{};
|
GLenum primitive_mode{};
|
||||||
TextureBufferUsage texture_buffer_usage{};
|
|
||||||
|
|
||||||
bool operator==(const ProgramVariant& rhs) const {
|
bool operator==(const ProgramVariant& rhs) const {
|
||||||
return std::tie(base_bindings, primitive_mode, texture_buffer_usage) ==
|
return std::tie(base_bindings, primitive_mode) ==
|
||||||
std::tie(rhs.base_bindings, rhs.primitive_mode, rhs.texture_buffer_usage);
|
std::tie(rhs.base_bindings, rhs.primitive_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const ProgramVariant& rhs) const {
|
bool operator!=(const ProgramVariant& rhs) const {
|
||||||
|
|
@ -112,7 +109,6 @@ template <>
|
||||||
struct hash<OpenGL::ProgramVariant> {
|
struct hash<OpenGL::ProgramVariant> {
|
||||||
std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
|
std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
|
||||||
return std::hash<OpenGL::BaseBindings>()(variant.base_bindings) ^
|
return std::hash<OpenGL::BaseBindings>()(variant.base_bindings) ^
|
||||||
std::hash<OpenGL::TextureBufferUsage>()(variant.texture_buffer_usage) ^
|
|
||||||
(static_cast<std::size_t>(variant.primitive_mode) << 6);
|
(static_cast<std::size_t>(variant.primitive_mode) << 6);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||||
}
|
}
|
||||||
const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
|
const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
|
||||||
|
|
||||||
const auto& sampler =
|
const SamplerInfo info{TextureType::Texture2D, false, depth_compare};
|
||||||
GetSampler(instr.sampler, {{TextureType::Texture2D, false, depth_compare}});
|
const auto& sampler = GetSampler(instr.sampler, info);
|
||||||
|
|
||||||
Node4 values;
|
Node4 values;
|
||||||
for (u32 element = 0; element < values.size(); ++element) {
|
for (u32 element = 0; element < values.size(); ++element) {
|
||||||
|
|
@ -149,7 +149,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||||
// Sadly, not all texture instructions specify the type of texture their sampler
|
// Sadly, not all texture instructions specify the type of texture their sampler
|
||||||
// uses. This must be fixed at a later instance.
|
// uses. This must be fixed at a later instance.
|
||||||
const auto& sampler =
|
const auto& sampler =
|
||||||
is_bindless ? GetBindlessSampler(instr.gpr8, {}) : GetSampler(instr.sampler, {});
|
is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler);
|
||||||
|
|
||||||
u32 indexer = 0;
|
u32 indexer = 0;
|
||||||
switch (instr.txq.query_type) {
|
switch (instr.txq.query_type) {
|
||||||
|
|
@ -185,8 +185,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||||
auto texture_type = instr.tmml.texture_type.Value();
|
auto texture_type = instr.tmml.texture_type.Value();
|
||||||
const bool is_array = instr.tmml.array != 0;
|
const bool is_array = instr.tmml.array != 0;
|
||||||
const auto& sampler =
|
const auto& sampler =
|
||||||
is_bindless ? GetBindlessSampler(instr.gpr20, {{texture_type, is_array, false}})
|
is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler);
|
||||||
: GetSampler(instr.sampler, {{texture_type, is_array, false}});
|
|
||||||
|
|
||||||
std::vector<Node> coords;
|
std::vector<Node> coords;
|
||||||
|
|
||||||
|
|
@ -254,67 +253,50 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset,
|
||||||
|
std::optional<u32> buffer) {
|
||||||
|
if (sampler_info) {
|
||||||
|
return *sampler_info;
|
||||||
|
}
|
||||||
|
const auto sampler =
|
||||||
|
buffer ? locker.ObtainBindlessSampler(*buffer, offset) : locker.ObtainBoundSampler(offset);
|
||||||
|
if (!sampler) {
|
||||||
|
LOG_WARNING(HW_GPU, "Unknown sampler info");
|
||||||
|
return SamplerInfo{TextureType::Texture2D, false, false, false};
|
||||||
|
}
|
||||||
|
return SamplerInfo{sampler->texture_type, sampler->is_array != 0, sampler->is_shadow != 0,
|
||||||
|
sampler->is_buffer != 0};
|
||||||
|
}
|
||||||
|
|
||||||
const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
|
const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
|
||||||
std::optional<SamplerInfo> sampler_info) {
|
std::optional<SamplerInfo> sampler_info) {
|
||||||
const auto offset = static_cast<u32>(sampler.index.Value());
|
const auto offset = static_cast<u32>(sampler.index.Value());
|
||||||
|
const auto info = GetSamplerInfo(sampler_info, offset);
|
||||||
TextureType type;
|
|
||||||
bool is_array;
|
|
||||||
bool is_shadow;
|
|
||||||
if (sampler_info) {
|
|
||||||
type = sampler_info->type;
|
|
||||||
is_array = sampler_info->is_array;
|
|
||||||
is_shadow = sampler_info->is_shadow;
|
|
||||||
} else if (const auto sampler = locker.ObtainBoundSampler(offset)) {
|
|
||||||
type = sampler->texture_type.Value();
|
|
||||||
is_array = sampler->is_array.Value() != 0;
|
|
||||||
is_shadow = sampler->is_shadow.Value() != 0;
|
|
||||||
} else {
|
|
||||||
LOG_WARNING(HW_GPU, "Unknown sampler info");
|
|
||||||
type = TextureType::Texture2D;
|
|
||||||
is_array = false;
|
|
||||||
is_shadow = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this sampler has already been used, return the existing mapping.
|
// If this sampler has already been used, return the existing mapping.
|
||||||
const auto it =
|
const auto it =
|
||||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||||
[offset](const Sampler& entry) { return entry.GetOffset() == offset; });
|
[offset](const Sampler& entry) { return entry.GetOffset() == offset; });
|
||||||
if (it != used_samplers.end()) {
|
if (it != used_samplers.end()) {
|
||||||
ASSERT(!it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
|
ASSERT(!it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
|
||||||
it->IsShadow() == is_shadow);
|
it->IsShadow() == info.is_shadow && it->IsBuffer() == info.is_buffer);
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise create a new mapping for this sampler
|
// Otherwise create a new mapping for this sampler
|
||||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||||
return used_samplers.emplace_back(Sampler(next_index, offset, type, is_array, is_shadow));
|
return used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow,
|
||||||
|
info.is_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
|
const Sampler& ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
|
||||||
std::optional<SamplerInfo> sampler_info) {
|
std::optional<SamplerInfo> sampler_info) {
|
||||||
const Node sampler_register = GetRegister(reg);
|
const Node sampler_register = GetRegister(reg);
|
||||||
const auto [base_sampler, buffer, offset] =
|
const auto [base_sampler, buffer, offset] =
|
||||||
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
||||||
ASSERT(base_sampler != nullptr);
|
ASSERT(base_sampler != nullptr);
|
||||||
|
|
||||||
TextureType type;
|
const auto info = GetSamplerInfo(sampler_info, offset, buffer);
|
||||||
bool is_array;
|
|
||||||
bool is_shadow;
|
|
||||||
if (sampler_info) {
|
|
||||||
type = sampler_info->type;
|
|
||||||
is_array = sampler_info->is_array;
|
|
||||||
is_shadow = sampler_info->is_shadow;
|
|
||||||
} else if (const auto sampler = locker.ObtainBindlessSampler(buffer, offset)) {
|
|
||||||
type = sampler->texture_type.Value();
|
|
||||||
is_array = sampler->is_array.Value() != 0;
|
|
||||||
is_shadow = sampler->is_shadow.Value() != 0;
|
|
||||||
} else {
|
|
||||||
LOG_WARNING(HW_GPU, "Unknown sampler info");
|
|
||||||
type = TextureType::Texture2D;
|
|
||||||
is_array = false;
|
|
||||||
is_shadow = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this sampler has already been used, return the existing mapping.
|
// If this sampler has already been used, return the existing mapping.
|
||||||
const auto it =
|
const auto it =
|
||||||
|
|
@ -323,15 +305,15 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
|
||||||
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
|
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
|
||||||
});
|
});
|
||||||
if (it != used_samplers.end()) {
|
if (it != used_samplers.end()) {
|
||||||
ASSERT(it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
|
ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
|
||||||
it->IsShadow() == is_shadow);
|
it->IsShadow() == info.is_shadow);
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise create a new mapping for this sampler
|
// Otherwise create a new mapping for this sampler
|
||||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||||
return used_samplers.emplace_back(
|
return used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
|
||||||
Sampler(next_index, offset, buffer, type, is_array, is_shadow));
|
info.is_shadow, info.is_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
|
void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
|
||||||
|
|
@ -416,17 +398,16 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||||
(texture_type == TextureType::TextureCube && is_array && is_shadow),
|
(texture_type == TextureType::TextureCube && is_array && is_shadow),
|
||||||
"This method is not supported.");
|
"This method is not supported.");
|
||||||
|
|
||||||
|
const SamplerInfo info{texture_type, is_array, is_shadow, false};
|
||||||
const auto& sampler =
|
const auto& sampler =
|
||||||
is_bindless ? GetBindlessSampler(*bindless_reg, {{texture_type, is_array, is_shadow}})
|
is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info);
|
||||||
: GetSampler(instr.sampler, {{texture_type, is_array, is_shadow}});
|
|
||||||
|
|
||||||
const bool lod_needed = process_mode == TextureProcessMode::LZ ||
|
const bool lod_needed = process_mode == TextureProcessMode::LZ ||
|
||||||
process_mode == TextureProcessMode::LL ||
|
process_mode == TextureProcessMode::LL ||
|
||||||
process_mode == TextureProcessMode::LLA;
|
process_mode == TextureProcessMode::LLA;
|
||||||
|
|
||||||
// LOD selection (either via bias or explicit textureLod) not
|
// LOD selection (either via bias or explicit textureLod) not supported in GL for
|
||||||
// supported in GL for sampler2DArrayShadow and
|
// sampler2DArrayShadow and samplerCubeArrayShadow.
|
||||||
// samplerCubeArrayShadow.
|
|
||||||
const bool gl_lod_supported =
|
const bool gl_lod_supported =
|
||||||
!((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
|
!((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
|
||||||
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
|
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
|
||||||
|
|
@ -436,8 +417,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||||
|
|
||||||
UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
|
UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
|
||||||
|
|
||||||
Node bias = {};
|
Node bias;
|
||||||
Node lod = {};
|
Node lod;
|
||||||
if (process_mode != TextureProcessMode::None && gl_lod_supported) {
|
if (process_mode != TextureProcessMode::None && gl_lod_supported) {
|
||||||
switch (process_mode) {
|
switch (process_mode) {
|
||||||
case TextureProcessMode::LZ:
|
case TextureProcessMode::LZ:
|
||||||
|
|
@ -573,10 +554,9 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
|
||||||
|
|
||||||
u64 parameter_register = instr.gpr20.Value();
|
u64 parameter_register = instr.gpr20.Value();
|
||||||
|
|
||||||
const auto& sampler =
|
const SamplerInfo info{texture_type, is_array, depth_compare, false};
|
||||||
is_bindless
|
const auto& sampler = is_bindless ? GetBindlessSampler(parameter_register++, info)
|
||||||
? GetBindlessSampler(parameter_register++, {{texture_type, is_array, depth_compare}})
|
: GetSampler(instr.sampler, info);
|
||||||
: GetSampler(instr.sampler, {{texture_type, is_array, depth_compare}});
|
|
||||||
|
|
||||||
std::vector<Node> aoffi;
|
std::vector<Node> aoffi;
|
||||||
if (is_aoffi) {
|
if (is_aoffi) {
|
||||||
|
|
@ -623,7 +603,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
|
||||||
// const Node aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr};
|
// const Node aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr};
|
||||||
// const Node multisample{is_multisample ? GetRegister(gpr20_cursor++) : nullptr};
|
// const Node multisample{is_multisample ? GetRegister(gpr20_cursor++) : nullptr};
|
||||||
|
|
||||||
const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, false}});
|
const auto& sampler = GetSampler(instr.sampler);
|
||||||
|
|
||||||
Node4 values;
|
Node4 values;
|
||||||
for (u32 element = 0; element < values.size(); ++element) {
|
for (u32 element = 0; element < values.size(); ++element) {
|
||||||
|
|
@ -659,7 +639,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
||||||
// When lod is used always is in gpr20
|
// When lod is used always is in gpr20
|
||||||
const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
|
const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
|
||||||
|
|
||||||
const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, false}});
|
const auto& sampler = GetSampler(instr.sampler);
|
||||||
|
|
||||||
Node4 values;
|
Node4 values;
|
||||||
for (u32 element = 0; element < values.size(); ++element) {
|
for (u32 element = 0; element < values.size(); ++element) {
|
||||||
|
|
|
||||||
|
|
@ -225,14 +225,15 @@ class Sampler {
|
||||||
public:
|
public:
|
||||||
/// This constructor is for bound samplers
|
/// This constructor is for bound samplers
|
||||||
constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
|
constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
|
||||||
bool is_array, bool is_shadow)
|
bool is_array, bool is_shadow, bool is_buffer)
|
||||||
: index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow} {}
|
: index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||||
|
is_buffer{is_buffer} {}
|
||||||
|
|
||||||
/// This constructor is for bindless samplers
|
/// This constructor is for bindless samplers
|
||||||
constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
|
constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
|
||||||
bool is_array, bool is_shadow)
|
bool is_array, bool is_shadow, bool is_buffer)
|
||||||
: index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
|
: index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
|
||||||
is_shadow{is_shadow}, is_bindless{true} {}
|
is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true} {}
|
||||||
|
|
||||||
constexpr u32 GetIndex() const {
|
constexpr u32 GetIndex() const {
|
||||||
return index;
|
return index;
|
||||||
|
|
@ -258,6 +259,10 @@ public:
|
||||||
return is_shadow;
|
return is_shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr bool IsBuffer() const {
|
||||||
|
return is_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr bool IsBindless() const {
|
constexpr bool IsBindless() const {
|
||||||
return is_bindless;
|
return is_bindless;
|
||||||
}
|
}
|
||||||
|
|
@ -270,6 +275,7 @@ private:
|
||||||
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
|
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
|
||||||
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
|
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
|
||||||
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
|
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
|
||||||
|
bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler.
|
||||||
bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
|
bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,7 @@ private:
|
||||||
Tegra::Shader::TextureType type;
|
Tegra::Shader::TextureType type;
|
||||||
bool is_array;
|
bool is_array;
|
||||||
bool is_shadow;
|
bool is_shadow;
|
||||||
|
bool is_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Decode();
|
void Decode();
|
||||||
|
|
@ -303,13 +304,17 @@ private:
|
||||||
/// Returns a predicate combiner operation
|
/// Returns a predicate combiner operation
|
||||||
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
|
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
|
||||||
|
|
||||||
|
/// Queries the missing sampler info from the execution context.
|
||||||
|
SamplerInfo GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset,
|
||||||
|
std::optional<u32> buffer = std::nullopt);
|
||||||
|
|
||||||
/// Accesses a texture sampler
|
/// Accesses a texture sampler
|
||||||
const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
|
const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
|
||||||
std::optional<SamplerInfo> sampler_info);
|
std::optional<SamplerInfo> sampler_info = std::nullopt);
|
||||||
|
|
||||||
// Accesses a texture sampler for a bindless texture.
|
/// Accesses a texture sampler for a bindless texture.
|
||||||
const Sampler& GetBindlessSampler(const Tegra::Shader::Register& reg,
|
const Sampler& GetBindlessSampler(Tegra::Shader::Register reg,
|
||||||
std::optional<SamplerInfo> sampler_info);
|
std::optional<SamplerInfo> sampler_info = std::nullopt);
|
||||||
|
|
||||||
/// Accesses an image.
|
/// Accesses an image.
|
||||||
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
|
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue