Files
Xenia-Canary/src/xenia/gpu/gl4/gl4_shader.cc
2015-12-22 20:15:07 -06:00

203 lines
6.4 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/gl4/gl4_shader.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
namespace xe {
namespace gpu {
namespace gl4 {
GL4Shader::GL4Shader(ShaderType shader_type, uint64_t data_hash,
const uint32_t* dword_ptr, uint32_t dword_count)
: Shader(shader_type, data_hash, dword_ptr, dword_count),
program_(0),
vao_(0) {}
GL4Shader::~GL4Shader() {
glDeleteProgram(program_);
glDeleteVertexArrays(1, &vao_);
}
bool GL4Shader::Prepare() {
if (!Shader::Prepare()) {
return false;
}
// Build static vertex array descriptor.
if (!PrepareVertexArrayObject()) {
XELOGE("Unable to prepare vertex shader array object");
return false;
}
if (!CompileProgram()) {
return false;
}
return true;
}
bool GL4Shader::PrepareVertexArrayObject() {
glCreateVertexArrays(1, &vao_);
for (const auto& vertex_binding : vertex_bindings()) {
for (const auto& attrib : vertex_binding.attributes) {
auto comp_count = GetVertexFormatComponentCount(
attrib.fetch_instr.attributes.data_format);
GLenum comp_type;
bool is_signed = attrib.fetch_instr.attributes.is_signed;
switch (attrib.fetch_instr.attributes.data_format) {
case VertexFormat::k_8_8_8_8:
comp_type = is_signed ? GL_BYTE : GL_UNSIGNED_BYTE;
break;
case VertexFormat::k_2_10_10_10:
comp_type = is_signed ? GL_INT_2_10_10_10_REV
: GL_UNSIGNED_INT_2_10_10_10_REV;
break;
case VertexFormat::k_10_11_11:
// assert_false(is_signed);
XELOGW("Signed k_10_11_11 vertex format not supported by GL");
comp_type = GL_UNSIGNED_INT_10F_11F_11F_REV;
break;
/*case VertexFormat::k_11_11_10:
break;*/
case VertexFormat::k_16_16:
comp_type = is_signed ? GL_SHORT : GL_UNSIGNED_SHORT;
break;
case VertexFormat::k_16_16_FLOAT:
comp_type = GL_HALF_FLOAT;
break;
case VertexFormat::k_16_16_16_16:
comp_type = is_signed ? GL_SHORT : GL_UNSIGNED_SHORT;
break;
case VertexFormat::k_16_16_16_16_FLOAT:
comp_type = GL_HALF_FLOAT;
break;
case VertexFormat::k_32:
comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT;
break;
case VertexFormat::k_32_32:
comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT;
break;
case VertexFormat::k_32_32_32_32:
comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT;
break;
case VertexFormat::k_32_FLOAT:
comp_type = GL_FLOAT;
break;
case VertexFormat::k_32_32_FLOAT:
comp_type = GL_FLOAT;
break;
case VertexFormat::k_32_32_32_FLOAT:
comp_type = GL_FLOAT;
break;
case VertexFormat::k_32_32_32_32_FLOAT:
comp_type = GL_FLOAT;
break;
default:
assert_unhandled_case(attrib.fetch_instr.attributes.data_format);
return false;
}
glEnableVertexArrayAttrib(vao_, attrib.attrib_index);
glVertexArrayAttribBinding(vao_, attrib.attrib_index,
vertex_binding.binding_index);
glVertexArrayAttribFormat(vao_, attrib.attrib_index, comp_count,
comp_type,
!attrib.fetch_instr.attributes.is_integer,
attrib.fetch_instr.attributes.offset * 4);
}
}
return true;
}
bool GL4Shader::CompileProgram() {
assert_zero(program_);
// Give source to GL.
auto source_str = GetTranslatedBinaryString();
auto source_str_ptr = source_str.c_str();
program_ = glCreateShaderProgramv(shader_type_ == ShaderType::kVertex
? GL_VERTEX_SHADER
: GL_FRAGMENT_SHADER,
1, &source_str_ptr);
if (!program_) {
XELOGE("Unable to create shader program");
return false;
}
// Output info log.
// log_length includes the null character.
GLint log_length = 0;
glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length);
std::string info_log;
if (log_length > 0) {
info_log.resize(log_length - 1);
glGetProgramInfoLog(program_, log_length, &log_length, &info_log[0]);
}
if (!info_log.empty()) {
XELOGD("Shader log: %s", info_log.c_str());
}
// Get error log, if we failed to link.
GLint link_status = 0;
glGetProgramiv(program_, GL_LINK_STATUS, &link_status);
if (!link_status) {
host_error_log_ = std::move(info_log);
assert_always("Unable to link generated shader");
return false;
}
// Get program binary, if it's available.
GLint binary_length = 0;
glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binary_length);
if (binary_length) {
host_binary_.resize(binary_length);
GLenum binary_format;
glGetProgramBinary(program_, binary_length, &binary_length, &binary_format,
host_binary_.data());
// If we are on nvidia, we can find the disassembly string.
// I haven't been able to figure out from the format how to do this
// without a search like this.
const char* disasm_start = nullptr;
size_t search_offset = 0;
char* search_start = reinterpret_cast<char*>(host_binary_.data());
while (true) {
auto p = reinterpret_cast<char*>(
memchr(host_binary_.data() + search_offset, '!',
host_binary_.size() - search_offset));
if (!p) {
break;
}
if (p[0] == '!' && p[1] == '!' && p[2] == 'N' && p[3] == 'V') {
disasm_start = p;
break;
}
search_offset = p - search_start;
++search_offset;
}
if (disasm_start) {
host_disassembly_ = std::string(disasm_start);
} else {
host_disassembly_ = std::string("Shader disassembly not available.");
}
}
return true;
}
} // namespace gl4
} // namespace gpu
} // namespace xe