Merge pull request #333 from bunnei/const-buff-hints
shaders: Expose hints about used const buffers.
This commit is contained in:
commit
34264879b3
|
@ -140,6 +140,11 @@ public:
|
||||||
return declarations.GetResult() + shader.GetResult();
|
return declarations.GetResult() + shader.GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns entries in the shader that are useful for external functions
|
||||||
|
ShaderEntries GetEntries() const {
|
||||||
|
return {GetConstBuffersDeclarations()};
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Gets the Subroutine object corresponding to the specified address.
|
/// Gets the Subroutine object corresponding to the specified address.
|
||||||
const Subroutine& GetSubroutine(u32 begin, u32 end) const {
|
const Subroutine& GetSubroutine(u32 begin, u32 end) const {
|
||||||
|
@ -186,10 +191,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates code representing a uniform (C buffer) register.
|
/// Generates code representing a uniform (C buffer) register.
|
||||||
std::string GetUniform(const Uniform& reg) const {
|
std::string GetUniform(const Uniform& reg) {
|
||||||
std::string index = std::to_string(reg.index);
|
declr_const_buffers[reg.index].MarkAsUsed(reg.index, reg.offset);
|
||||||
return "uniform_" + index + "[" + std::to_string(reg.offset >> 2) + "][" +
|
return 'c' + std::to_string(reg.index) + '[' + std::to_string(reg.offset) + ']';
|
||||||
std::to_string(reg.offset & 3) + "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -439,6 +443,14 @@ private:
|
||||||
GenerateDeclarations();
|
GenerateDeclarations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of constant buffer declarations
|
||||||
|
std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
|
||||||
|
std::vector<ConstBufferEntry> result;
|
||||||
|
std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
|
||||||
|
std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// Add declarations for registers
|
/// Add declarations for registers
|
||||||
void GenerateDeclarations() {
|
void GenerateDeclarations() {
|
||||||
for (const auto& reg : declr_register) {
|
for (const auto& reg : declr_register) {
|
||||||
|
@ -463,6 +475,17 @@ private:
|
||||||
") out vec4 " + GetOutputAttribute(index) + ";");
|
") out vec4 " + GetOutputAttribute(index) + ";");
|
||||||
}
|
}
|
||||||
declarations.AddLine("");
|
declarations.AddLine("");
|
||||||
|
|
||||||
|
unsigned const_buffer_layout = 0;
|
||||||
|
for (const auto& entry : GetConstBuffersDeclarations()) {
|
||||||
|
declarations.AddLine("layout(std430, binding = " + std::to_string(const_buffer_layout) +
|
||||||
|
") buffer c" + std::to_string(entry.GetIndex()) + "_buffer");
|
||||||
|
declarations.AddLine("{");
|
||||||
|
declarations.AddLine(" float c" + std::to_string(entry.GetIndex()) + "[];");
|
||||||
|
declarations.AddLine("};");
|
||||||
|
declarations.AddLine("");
|
||||||
|
++const_buffer_layout;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -478,18 +501,19 @@ private:
|
||||||
std::set<std::string> declr_register;
|
std::set<std::string> declr_register;
|
||||||
std::set<Attribute::Index> declr_input_attribute;
|
std::set<Attribute::Index> declr_input_attribute;
|
||||||
std::set<Attribute::Index> declr_output_attribute;
|
std::set<Attribute::Index> declr_output_attribute;
|
||||||
}; // namespace Decompiler
|
std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers;
|
||||||
|
};
|
||||||
|
|
||||||
std::string GetCommonDeclarations() {
|
std::string GetCommonDeclarations() {
|
||||||
return "bool exec_shader();";
|
return "bool exec_shader();";
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
|
boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
|
||||||
Maxwell3D::Regs::ShaderStage stage) {
|
Maxwell3D::Regs::ShaderStage stage) {
|
||||||
try {
|
try {
|
||||||
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
|
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).GetSubroutines();
|
||||||
GLSLGenerator generator(subroutines, program_code, main_offset, stage);
|
GLSLGenerator generator(subroutines, program_code, main_offset, stage);
|
||||||
return generator.GetShaderCode();
|
return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
|
||||||
} catch (const DecompileFail& exception) {
|
} catch (const DecompileFail& exception) {
|
||||||
LOG_ERROR(HW_GPU, "Shader decompilation failed: %s", exception.what());
|
LOG_ERROR(HW_GPU, "Shader decompilation failed: %s", exception.what());
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ using Tegra::Engines::Maxwell3D;
|
||||||
|
|
||||||
std::string GetCommonDeclarations();
|
std::string GetCommonDeclarations();
|
||||||
|
|
||||||
boost::optional<std::string> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
|
boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
|
||||||
Maxwell3D::Regs::ShaderStage stage);
|
Maxwell3D::Regs::ShaderStage stage);
|
||||||
|
|
||||||
} // namespace Decompiler
|
} // namespace Decompiler
|
||||||
} // namespace GLShader
|
} // namespace GLShader
|
||||||
|
|
|
@ -3,18 +3,60 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||||
|
|
||||||
namespace GLShader {
|
namespace GLShader {
|
||||||
|
|
||||||
std::string GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config) {
|
using Tegra::Engines::Maxwell3D;
|
||||||
UNREACHABLE();
|
|
||||||
return {};
|
static constexpr u32 PROGRAM_OFFSET{10};
|
||||||
|
|
||||||
|
ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config) {
|
||||||
|
std::string out = "#version 430 core\n";
|
||||||
|
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||||
|
out += Decompiler::GetCommonDeclarations();
|
||||||
|
|
||||||
|
ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
|
||||||
|
Maxwell3D::Regs::ShaderStage::Vertex)
|
||||||
|
.get_value_or({});
|
||||||
|
out += R"(
|
||||||
|
|
||||||
|
out gl_PerVertex {
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
exec_shader();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config) {
|
)";
|
||||||
UNREACHABLE();
|
out += program.first;
|
||||||
return {};
|
return {out, program.second};
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config) {
|
||||||
|
std::string out = "#version 430 core\n";
|
||||||
|
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||||
|
out += Decompiler::GetCommonDeclarations();
|
||||||
|
|
||||||
|
ProgramResult program = Decompiler::DecompileProgram(setup.program_code, PROGRAM_OFFSET,
|
||||||
|
Maxwell3D::Regs::ShaderStage::Fragment)
|
||||||
|
.get_value_or({});
|
||||||
|
out += R"(
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
uniform sampler2D tex[32];
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
exec_shader();
|
||||||
|
}
|
||||||
|
|
||||||
|
)";
|
||||||
|
out += program.first;
|
||||||
|
return {out, program.second};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace GLShader
|
} // namespace GLShader
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
|
|
||||||
|
@ -16,6 +18,38 @@ constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
|
||||||
|
|
||||||
using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>;
|
using ProgramCode = std::array<u64, MAX_PROGRAM_CODE_LENGTH>;
|
||||||
|
|
||||||
|
class ConstBufferEntry {
|
||||||
|
public:
|
||||||
|
void MarkAsUsed(unsigned index, unsigned offset) {
|
||||||
|
is_used = true;
|
||||||
|
this->index = index;
|
||||||
|
max_offset = std::max(max_offset, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUsed() const {
|
||||||
|
return is_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned GetIndex() const {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned GetSize() const {
|
||||||
|
return max_offset + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_used{};
|
||||||
|
unsigned index{};
|
||||||
|
unsigned max_offset{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderEntries {
|
||||||
|
std::vector<ConstBufferEntry> const_buffer_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ProgramResult = std::pair<std::string, ShaderEntries>;
|
||||||
|
|
||||||
struct ShaderSetup {
|
struct ShaderSetup {
|
||||||
ShaderSetup(ProgramCode&& program_code) : program_code(std::move(program_code)) {}
|
ShaderSetup(ProgramCode&& program_code) : program_code(std::move(program_code)) {}
|
||||||
|
|
||||||
|
@ -58,13 +92,13 @@ struct MaxwellFSConfig : Common::HashableStruct<MaxwellShaderConfigCommon> {
|
||||||
* Generates the GLSL vertex shader program source code for the given VS program
|
* Generates the GLSL vertex shader program source code for the given VS program
|
||||||
* @returns String of the shader source code
|
* @returns String of the shader source code
|
||||||
*/
|
*/
|
||||||
std::string GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config);
|
ProgramResult GenerateVertexShader(const ShaderSetup& setup, const MaxwellVSConfig& config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the GLSL fragment shader program source code for the given FS program
|
* Generates the GLSL fragment shader program source code for the given FS program
|
||||||
* @returns String of the shader source code
|
* @returns String of the shader source code
|
||||||
*/
|
*/
|
||||||
std::string GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config);
|
ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSConfig& config);
|
||||||
|
|
||||||
} // namespace GLShader
|
} // namespace GLShader
|
||||||
|
|
||||||
|
|
|
@ -41,19 +41,25 @@ class OGLShaderStage {
|
||||||
public:
|
public:
|
||||||
OGLShaderStage() = default;
|
OGLShaderStage() = default;
|
||||||
|
|
||||||
void Create(const char* source, GLenum type) {
|
void Create(const ProgramResult& program_result, GLenum type) {
|
||||||
OGLShader shader;
|
OGLShader shader;
|
||||||
shader.Create(source, type);
|
shader.Create(program_result.first.c_str(), type);
|
||||||
program.Create(true, shader.handle);
|
program.Create(true, shader.handle);
|
||||||
Impl::SetShaderUniformBlockBindings(program.handle);
|
Impl::SetShaderUniformBlockBindings(program.handle);
|
||||||
Impl::SetShaderSamplerBindings(program.handle);
|
Impl::SetShaderSamplerBindings(program.handle);
|
||||||
|
entries = program_result.second;
|
||||||
}
|
}
|
||||||
GLuint GetHandle() const {
|
GLuint GetHandle() const {
|
||||||
return program.handle;
|
return program.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShaderEntries GetEntries() const {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OGLProgram program;
|
OGLProgram program;
|
||||||
|
ShaderEntries entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(wwylele): beautify this doc
|
// TODO(wwylele): beautify this doc
|
||||||
|
@ -61,25 +67,28 @@ private:
|
||||||
// The double cache is needed because diffent KeyConfigType, which includes a hash of the code
|
// The double cache is needed because diffent KeyConfigType, which includes a hash of the code
|
||||||
// region (including its leftover unused code) can generate the same GLSL code.
|
// region (including its leftover unused code) can generate the same GLSL code.
|
||||||
template <typename KeyConfigType,
|
template <typename KeyConfigType,
|
||||||
std::string (*CodeGenerator)(const ShaderSetup&, const KeyConfigType&), GLenum ShaderType>
|
ProgramResult (*CodeGenerator)(const ShaderSetup&, const KeyConfigType&),
|
||||||
|
GLenum ShaderType>
|
||||||
class ShaderCache {
|
class ShaderCache {
|
||||||
public:
|
public:
|
||||||
ShaderCache() = default;
|
ShaderCache() = default;
|
||||||
|
|
||||||
GLuint Get(const KeyConfigType& key, const ShaderSetup& setup) {
|
using Result = std::pair<GLuint, ShaderEntries>;
|
||||||
|
|
||||||
|
Result Get(const KeyConfigType& key, const ShaderSetup& setup) {
|
||||||
auto map_it = shader_map.find(key);
|
auto map_it = shader_map.find(key);
|
||||||
if (map_it == shader_map.end()) {
|
if (map_it == shader_map.end()) {
|
||||||
std::string program = CodeGenerator(setup, key);
|
ProgramResult program = CodeGenerator(setup, key);
|
||||||
|
|
||||||
auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{});
|
auto [iter, new_shader] = shader_cache.emplace(program.first, OGLShaderStage{});
|
||||||
OGLShaderStage& cached_shader = iter->second;
|
OGLShaderStage& cached_shader = iter->second;
|
||||||
if (new_shader) {
|
if (new_shader) {
|
||||||
cached_shader.Create(program.c_str(), ShaderType);
|
cached_shader.Create(program, ShaderType);
|
||||||
}
|
}
|
||||||
shader_map[key] = &cached_shader;
|
shader_map[key] = &cached_shader;
|
||||||
return cached_shader.GetHandle();
|
return {cached_shader.GetHandle(), program.second};
|
||||||
} else {
|
} else {
|
||||||
return map_it->second->GetHandle();
|
return {map_it->second->GetHandle(), map_it->second->GetEntries()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,12 +107,18 @@ public:
|
||||||
pipeline.Create();
|
pipeline.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UseProgrammableVertexShader(const MaxwellVSConfig& config, const ShaderSetup setup) {
|
ShaderEntries UseProgrammableVertexShader(const MaxwellVSConfig& config,
|
||||||
current.vs = vertex_shaders.Get(config, setup);
|
const ShaderSetup setup) {
|
||||||
|
ShaderEntries result;
|
||||||
|
std::tie(current.vs, result) = vertex_shaders.Get(config, setup);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UseProgrammableFragmentShader(const MaxwellFSConfig& config, const ShaderSetup setup) {
|
ShaderEntries UseProgrammableFragmentShader(const MaxwellFSConfig& config,
|
||||||
current.fs = fragment_shaders.Get(config, setup);
|
const ShaderSetup setup) {
|
||||||
|
ShaderEntries result;
|
||||||
|
std::tie(current.fs, result) = fragment_shaders.Get(config, setup);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UseTrivialGeometryShader() {
|
void UseTrivialGeometryShader() {
|
||||||
|
|
Loading…
Reference in a new issue