Merge pull request #333 from bunnei/const-buff-hints

shaders: Expose hints about used const buffers.
This commit is contained in:
bunnei 2018-04-15 12:12:17 -04:00 committed by GitHub
commit 34264879b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 31 deletions

View File

@ -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());
} }

View File

@ -17,7 +17,7 @@ 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

View File

@ -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

View File

@ -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

View File

@ -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() {