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.
This commit is contained in:
ReinUsesLisp 2019-11-06 04:32:43 -03:00
parent c52f37f259
commit 32c1bc6a67
No known key found for this signature in database
GPG key ID: 2DFC508897B39CFE
9 changed files with 109 additions and 176 deletions

View file

@ -271,9 +271,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
SetupDrawConstBuffers(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);
switch (program) {
@ -303,7 +303,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
// When VertexA is enabled, we have dual vertex shaders
if (program == Maxwell::ShaderProgram::VertexA) {
// VertexB was combined with VertexA, so we skip the VertexB iteration
index++;
++index;
}
base_bindings = next_bindings;
@ -732,11 +732,10 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
}
auto kernel = shader_cache.GetComputeKernel(code_addr);
ProgramVariant variant;
variant.texture_buffer_usage = SetupComputeTextures(kernel);
SetupComputeTextures(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.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));
}
TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage,
const Shader& shader,
BaseBindings base_bindings) {
void RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage, const Shader& shader,
BaseBindings base_bindings) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = system.GPU();
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),
"Exceeded the number of active textures.");
TextureBufferUsage texture_buffer_usage{0};
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint];
const auto texture = [&] {
@ -943,15 +939,11 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
return maxwell3d.GetTextureInfo(tex_handle);
}();
if (SetupTexture(base_bindings.sampler + bindpoint, texture, entry)) {
texture_buffer_usage.set(bindpoint);
}
SetupTexture(base_bindings.sampler + bindpoint, texture, entry);
}
return texture_buffer_usage;
}
TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& compute = system.GPU().KeplerCompute();
const auto& entries = kernel->GetShaderEntries().samplers;
@ -959,8 +951,6 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
ASSERT_MSG(entries.size() <= std::size(state.textures),
"Exceeded the number of active textures.");
TextureBufferUsage texture_buffer_usage{0};
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint];
const auto texture = [&] {
@ -972,34 +962,29 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
return compute.GetTextureInfo(tex_handle);
}();
if (SetupTexture(bindpoint, texture, entry)) {
texture_buffer_usage.set(bindpoint);
}
SetupTexture(bindpoint, texture, entry);
}
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) {
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
if (!view) {
// Can occur when texture addr is null or its memory is unmapped/invalid
state.samplers[binding] = 0;
state.textures[binding] = 0;
return false;
return;
}
state.textures[binding] = view->GetTexture();
if (view->GetSurfaceParams().IsBuffer()) {
return true;
return;
}
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
// Apply swizzle to textures that are not buffers.
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
texture.tic.w_source);
return false;
}
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {

View file

@ -107,16 +107,15 @@ private:
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
void DrawPrelude();
/// Configures the current textures to use for the draw command. Returns shaders texture buffer
/// usage.
TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader, BaseBindings base_bindings);
/// Configures the current textures to use for the draw command.
void SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
BaseBindings base_bindings);
/// Configures the textures used in a compute shader. Returns texture buffer usage.
TextureBufferUsage SetupComputeTextures(const Shader& kernel);
/// Configures the textures used in a compute shader.
void SetupComputeTextures(const Shader& kernel);
/// Configures a texture. Returns true when the texture is a texture buffer.
bool SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
/// Configures a texture.
void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
const GLShader::SamplerEntry& entry);
/// Configures images in a compute shader.

View file

@ -270,7 +270,6 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramTy
auto base_bindings{variant.base_bindings};
const auto primitive_mode{variant.primitive_mode};
const auto texture_buffer_usage{variant.texture_buffer_usage};
std::string source = fmt::format(R"(// {}
#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++);
}
// 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) {
const auto [glsl_topology, debug_name, max_vertices] =
GetPrimitiveDescription(primitive_mode);

View file

@ -658,9 +658,11 @@ private:
const std::string description{"layout (binding = SAMPLER_BINDING_" +
std::to_string(sampler.GetIndex()) + ") uniform"};
std::string sampler_type = [&]() {
if (sampler.IsBuffer()) {
return "samplerBuffer";
}
switch (sampler.GetType()) {
case Tegra::Shader::TextureType::Texture1D:
// Special cased, read below.
return "sampler1D";
case Tegra::Shader::TextureType::Texture2D:
return "sampler2D";
@ -680,19 +682,7 @@ private:
sampler_type += "Shadow";
}
if (sampler.GetType() == Tegra::Shader::TextureType::Texture1D) {
// 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);
}
code.AddLine("{} {} {};", description, sampler_type, name);
}
if (!samplers.empty()) {
code.AddNewLine();
@ -1749,27 +1739,14 @@ private:
expr += ", ";
}
// Store a copy of the expression without the lod to be used with texture buffers
std::string expr_buffer = expr;
if (meta->lod) {
if (meta->lod && !meta->sampler.IsBuffer()) {
expr += ", ";
expr += Visit(meta->lod).AsInt();
}
expr += ')';
expr += GetSwizzle(meta->element);
expr_buffer += ')';
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};
return {std::move(expr), Type::Float};
}
Expression ImageLoad(Operation operation) {
@ -2214,10 +2191,6 @@ private:
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 {
return fmt::format("{}_{}_{}", name, index, suffix);
}

View file

@ -28,23 +28,6 @@ using VideoCommon::Shader::KeyMap;
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>;
enum class TransferableEntryKind : u32 {
@ -52,10 +35,28 @@ enum class TransferableEntryKind : u32 {
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
static_assert(sizeof(BaseBindings) == 16);
static_assert(sizeof(ProgramVariant) == 20);
ShaderCacheVersionHash GetShaderCacheVersionHash() {
ShaderCacheVersionHash hash{};

View file

@ -4,7 +4,6 @@
#pragma once
#include <bitset>
#include <optional>
#include <string>
#include <tuple>
@ -37,7 +36,6 @@ struct ShaderDiskCacheDump;
using ProgramCode = std::vector<u64>;
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
using TextureBufferUsage = std::bitset<64>;
/// Allocated bindings used by an OpenGL shader program
struct BaseBindings {
@ -61,11 +59,10 @@ static_assert(std::is_trivially_copyable_v<BaseBindings>);
struct ProgramVariant {
BaseBindings base_bindings;
GLenum primitive_mode{};
TextureBufferUsage texture_buffer_usage{};
bool operator==(const ProgramVariant& rhs) const {
return std::tie(base_bindings, primitive_mode, texture_buffer_usage) ==
std::tie(rhs.base_bindings, rhs.primitive_mode, rhs.texture_buffer_usage);
return std::tie(base_bindings, primitive_mode) ==
std::tie(rhs.base_bindings, rhs.primitive_mode);
}
bool operator!=(const ProgramVariant& rhs) const {
@ -112,7 +109,6 @@ template <>
struct hash<OpenGL::ProgramVariant> {
std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
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);
}
};