[GL4] Farewell, GL4 backend

This commit is contained in:
Dr. Chat
2018-05-04 11:54:33 -05:00
parent f54ac240a4
commit 040d8d1ade
37 changed files with 2 additions and 7583 deletions

View File

@@ -1,535 +0,0 @@
/**
******************************************************************************
* 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/draw_batcher.h"
#include <cstring>
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
#include "xenia/gpu/gpu_flags.h"
namespace xe {
namespace gpu {
namespace gl4 {
using namespace xe::gpu::xenos;
const size_t kCommandBufferCapacity = 16 * (1024 * 1024);
const size_t kCommandBufferAlignment = 4;
const size_t kStateBufferCapacity = 64 * (1024 * 1024);
const size_t kStateBufferAlignment = 256;
DrawBatcher::DrawBatcher(RegisterFile* register_file)
: register_file_(register_file),
command_buffer_(kCommandBufferCapacity, kCommandBufferAlignment),
state_buffer_(kStateBufferCapacity, kStateBufferAlignment),
array_data_buffer_(nullptr),
draw_open_(false) {
std::memset(&batch_state_, 0, sizeof(batch_state_));
batch_state_.needs_reconfigure = true;
batch_state_.command_range_start = batch_state_.state_range_start =
UINTPTR_MAX;
std::memset(&active_draw_, 0, sizeof(active_draw_));
}
bool DrawBatcher::Initialize(CircularBuffer* array_data_buffer) {
array_data_buffer_ = array_data_buffer;
if (!command_buffer_.Initialize()) {
return false;
}
if (!state_buffer_.Initialize()) {
return false;
}
if (!InitializeTFB()) {
return false;
}
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command_buffer_.handle());
return true;
}
// Initializes a transform feedback object
// We use this to capture vertex data straight from the vertex/geometry shader.
bool DrawBatcher::InitializeTFB() {
glCreateBuffers(1, &tfvbo_);
if (!tfvbo_) {
return false;
}
glCreateTransformFeedbacks(1, &tfbo_);
if (!tfbo_) {
return false;
}
glCreateQueries(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 1, &tfqo_);
if (!tfqo_) {
return false;
}
// TODO(DrChat): Calculate this based on the number of primitives drawn.
glNamedBufferData(tfvbo_, 16384 * 4, nullptr, GL_STATIC_READ);
return true;
}
void DrawBatcher::ShutdownTFB() {
glDeleteBuffers(1, &tfvbo_);
glDeleteTransformFeedbacks(1, &tfbo_);
glDeleteQueries(1, &tfqo_);
tfvbo_ = 0;
tfbo_ = 0;
tfqo_ = 0;
}
size_t DrawBatcher::QueryTFBSize() {
if (!tfb_enabled_) {
return 0;
}
size_t size = 0;
switch (tfb_prim_type_gl_) {
case GL_POINTS:
size = tfb_prim_count_ * 1 * 4 * 4;
break;
case GL_LINES:
size = tfb_prim_count_ * 2 * 4 * 4;
break;
case GL_TRIANGLES:
size = tfb_prim_count_ * 3 * 4 * 4;
break;
}
return size;
}
bool DrawBatcher::ReadbackTFB(void* buffer, size_t size) {
if (!tfb_enabled_) {
XELOGW("DrawBatcher::ReadbackTFB called when TFB was disabled!");
return false;
}
void* data = glMapNamedBufferRange(tfvbo_, 0, size, GL_MAP_READ_BIT);
std::memcpy(buffer, data, size);
glUnmapNamedBuffer(tfvbo_);
return true;
}
void DrawBatcher::Shutdown() {
command_buffer_.Shutdown();
state_buffer_.Shutdown();
ShutdownTFB();
}
bool DrawBatcher::ReconfigurePipeline(GL4Shader* vertex_shader,
GL4Shader* pixel_shader,
GLuint pipeline) {
if (batch_state_.pipeline == pipeline) {
// No-op.
return true;
}
if (!Flush(FlushMode::kReconfigure)) {
return false;
}
batch_state_.vertex_shader = vertex_shader;
batch_state_.pixel_shader = pixel_shader;
batch_state_.pipeline = pipeline;
return true;
}
bool DrawBatcher::BeginDrawArrays(PrimitiveType prim_type,
uint32_t index_count) {
assert_false(draw_open_);
if (batch_state_.prim_type != prim_type || batch_state_.indexed) {
if (!Flush(FlushMode::kReconfigure)) {
return false;
}
}
batch_state_.prim_type = prim_type;
batch_state_.indexed = false;
if (!BeginDraw()) {
return false;
}
auto cmd = active_draw_.draw_arrays_cmd;
cmd->base_instance = 0;
cmd->instance_count = 1;
cmd->count = index_count;
cmd->first_index = 0;
return true;
}
bool DrawBatcher::BeginDrawElements(PrimitiveType prim_type,
uint32_t index_count,
IndexFormat index_format) {
assert_false(draw_open_);
GLenum index_type =
index_format == IndexFormat::kInt32 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
if (batch_state_.prim_type != prim_type || !batch_state_.indexed ||
batch_state_.index_type != index_type) {
if (!Flush(FlushMode::kReconfigure)) {
return false;
}
}
batch_state_.prim_type = prim_type;
batch_state_.indexed = true;
batch_state_.index_type = index_type;
if (!BeginDraw()) {
return false;
}
uint32_t start_index = register_file_->values[XE_GPU_REG_VGT_INDX_OFFSET].u32;
assert_zero(start_index);
auto cmd = active_draw_.draw_elements_cmd;
cmd->base_instance = 0;
cmd->instance_count = 1;
cmd->count = index_count;
cmd->first_index = start_index;
cmd->base_vertex = 0;
return true;
}
bool DrawBatcher::BeginDraw() {
draw_open_ = true;
if (batch_state_.needs_reconfigure) {
batch_state_.needs_reconfigure = false;
// Have been reconfigured since last draw - need to compute state size.
// Layout:
// [draw command]
// [common header]
// [consts]
// Padded to max.
GLsizei command_size = 0;
if (batch_state_.indexed) {
command_size = sizeof(DrawElementsIndirectCommand);
} else {
command_size = sizeof(DrawArraysIndirectCommand);
}
batch_state_.command_stride =
xe::round_up(command_size, GLsizei(kCommandBufferAlignment));
GLsizei header_size = sizeof(CommonHeader);
// TODO(benvanik): consts sizing.
// GLsizei float_consts_size = sizeof(float4) * 512;
// GLsizei bool_consts_size = sizeof(uint32_t) * 8;
// GLsizei loop_consts_size = sizeof(uint32_t) * 32;
// GLsizei consts_size =
// float_consts_size + bool_consts_size + loop_consts_size;
// batch_state_.float_consts_offset = batch_state_.header_offset +
// header_size;
// batch_state_.bool_consts_offset =
// batch_state_.float_consts_offset + float_consts_size;
// batch_state_.loop_consts_offset =
// batch_state_.bool_consts_offset + bool_consts_size;
GLsizei consts_size = 0;
batch_state_.state_stride = header_size + consts_size;
}
// Allocate a command data block.
// We should treat it as write-only.
if (!command_buffer_.CanAcquire(batch_state_.command_stride)) {
Flush(FlushMode::kMakeCoherent);
}
active_draw_.command_allocation =
command_buffer_.Acquire(batch_state_.command_stride);
assert_not_null(active_draw_.command_allocation.host_ptr);
// Allocate a state data block.
// We should treat it as write-only.
if (!state_buffer_.CanAcquire(batch_state_.state_stride)) {
Flush(FlushMode::kMakeCoherent);
}
active_draw_.state_allocation =
state_buffer_.Acquire(batch_state_.state_stride);
assert_not_null(active_draw_.state_allocation.host_ptr);
active_draw_.command_address =
reinterpret_cast<uintptr_t>(active_draw_.command_allocation.host_ptr);
auto state_host_ptr =
reinterpret_cast<uintptr_t>(active_draw_.state_allocation.host_ptr);
active_draw_.header = reinterpret_cast<CommonHeader*>(state_host_ptr);
active_draw_.header->ps_param_gen = -1;
// active_draw_.float_consts =
// reinterpret_cast<float4*>(state_host_ptr +
// batch_state_.float_consts_offset);
// active_draw_.bool_consts =
// reinterpret_cast<uint32_t*>(state_host_ptr +
// batch_state_.bool_consts_offset);
// active_draw_.loop_consts =
// reinterpret_cast<uint32_t*>(state_host_ptr +
// batch_state_.loop_consts_offset);
return true;
}
void DrawBatcher::DiscardDraw() {
if (!draw_open_) {
// No-op.
return;
}
draw_open_ = false;
command_buffer_.Discard(std::move(active_draw_.command_allocation));
state_buffer_.Discard(std::move(active_draw_.state_allocation));
}
bool DrawBatcher::CommitDraw() {
assert_true(draw_open_);
draw_open_ = false;
// Copy over required constants.
CopyConstants();
if (batch_state_.state_range_start == UINTPTR_MAX) {
batch_state_.command_range_start = active_draw_.command_allocation.offset;
batch_state_.state_range_start = active_draw_.state_allocation.offset;
}
batch_state_.command_range_length +=
active_draw_.command_allocation.aligned_length;
batch_state_.state_range_length +=
active_draw_.state_allocation.aligned_length;
command_buffer_.Commit(std::move(active_draw_.command_allocation));
state_buffer_.Commit(std::move(active_draw_.state_allocation));
++batch_state_.draw_count;
return true;
}
void DrawBatcher::TFBBegin(PrimitiveType prim_type) {
if (!tfb_enabled_) {
return;
}
// Translate the primitive typename to something compatible with TFB.
GLenum gl_prim_type = 0;
switch (prim_type) {
case PrimitiveType::kLineList:
gl_prim_type = GL_LINES;
break;
case PrimitiveType::kLineStrip:
gl_prim_type = GL_LINES;
break;
case PrimitiveType::kLineLoop:
gl_prim_type = GL_LINES;
break;
case PrimitiveType::kPointList:
// The geometry shader associated with this writes out triangles.
gl_prim_type = GL_TRIANGLES;
break;
case PrimitiveType::kTriangleList:
gl_prim_type = GL_TRIANGLES;
break;
case PrimitiveType::kTriangleStrip:
gl_prim_type = GL_TRIANGLES;
break;
case PrimitiveType::kRectangleList:
gl_prim_type = GL_TRIANGLES;
break;
case PrimitiveType::kTriangleFan:
gl_prim_type = GL_TRIANGLES;
break;
case PrimitiveType::kQuadList:
// FIXME: In some cases the geometry shader will output lines.
// See: GL4CommandProcessor::UpdateShaders
gl_prim_type = GL_TRIANGLES;
break;
default:
assert_unhandled_case(prim_type);
break;
}
// TODO(DrChat): Resize the TFVBO here.
// Could draw a 2nd time with the rasterizer disabled once we have a primitive
// count.
tfb_prim_type_ = prim_type;
tfb_prim_type_gl_ = gl_prim_type;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfbo_);
// Bind the buffer to the TFB object.
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfvbo_);
// Begin a query for # prims written
glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0, tfqo_);
// Begin capturing.
glBeginTransformFeedback(gl_prim_type);
}
void DrawBatcher::TFBEnd() {
if (!tfb_enabled_) {
return;
}
glEndTransformFeedback();
glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
// Cache the query size as query objects aren't shared.
GLint prim_count = 0;
glGetQueryObjectiv(tfqo_, GL_QUERY_RESULT, &prim_count);
tfb_prim_count_ = prim_count;
}
bool DrawBatcher::Flush(FlushMode mode) {
GLboolean cull_enabled = 0;
if (batch_state_.draw_count) {
#if FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // FINE_GRAINED_DRAW_SCOPES
assert_not_zero(batch_state_.command_stride);
assert_not_zero(batch_state_.state_stride);
// Flush pending buffer changes.
command_buffer_.Flush();
state_buffer_.Flush();
array_data_buffer_->Flush();
// State data is indexed by draw ID.
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, state_buffer_.handle(),
batch_state_.state_range_start,
batch_state_.state_range_length);
GLenum prim_type = 0;
bool valid_prim = true;
switch (batch_state_.prim_type) {
case PrimitiveType::kPointList:
prim_type = GL_POINTS;
break;
case PrimitiveType::kLineList:
prim_type = GL_LINES;
break;
case PrimitiveType::kLineStrip:
prim_type = GL_LINE_STRIP;
break;
case PrimitiveType::kLineLoop:
prim_type = GL_LINE_LOOP;
break;
case PrimitiveType::kTriangleList:
prim_type = GL_TRIANGLES;
break;
case PrimitiveType::kTriangleStrip:
prim_type = GL_TRIANGLE_STRIP;
break;
case PrimitiveType::kTriangleFan:
prim_type = GL_TRIANGLE_FAN;
break;
case PrimitiveType::kRectangleList:
prim_type = GL_TRIANGLES;
// Rect lists aren't culled. There may be other things they skip too.
// assert_true(
// (register_file_->values[XE_GPU_REG_PA_SU_SC_MODE_CNTL].u32
// & 0x3) == 0);
break;
case PrimitiveType::kQuadList:
prim_type = GL_LINES_ADJACENCY;
break;
default:
case PrimitiveType::kTriangleWithWFlags:
prim_type = GL_TRIANGLES;
valid_prim = false;
XELOGE("unsupported primitive type %d", batch_state_.prim_type);
assert_unhandled_case(batch_state_.prim_type);
break;
}
// Fast path for single draws.
void* indirect_offset =
reinterpret_cast<void*>(batch_state_.command_range_start);
if (tfb_enabled_) {
TFBBegin(batch_state_.prim_type);
}
if (valid_prim && batch_state_.draw_count == 1) {
// Fast path for one draw. Removes MDI overhead when not required.
if (batch_state_.indexed) {
auto& cmd = active_draw_.draw_elements_cmd;
glDrawElementsInstancedBaseVertexBaseInstance(
prim_type, cmd->count, batch_state_.index_type,
reinterpret_cast<void*>(
uintptr_t(cmd->first_index) *
(batch_state_.index_type == GL_UNSIGNED_SHORT ? 2 : 4)),
cmd->instance_count, cmd->base_vertex, cmd->base_instance);
} else {
auto& cmd = active_draw_.draw_arrays_cmd;
glDrawArraysInstancedBaseInstance(prim_type, cmd->first_index,
cmd->count, cmd->instance_count,
cmd->base_instance);
}
} else if (valid_prim) {
// Full multi-draw.
if (batch_state_.indexed) {
glMultiDrawElementsIndirect(prim_type, batch_state_.index_type,
indirect_offset, batch_state_.draw_count,
batch_state_.command_stride);
} else {
glMultiDrawArraysIndirect(prim_type, indirect_offset,
batch_state_.draw_count,
batch_state_.command_stride);
}
}
if (tfb_enabled_) {
TFBEnd();
}
batch_state_.command_range_start = UINTPTR_MAX;
batch_state_.command_range_length = 0;
batch_state_.state_range_start = UINTPTR_MAX;
batch_state_.state_range_length = 0;
batch_state_.draw_count = 0;
}
if (mode == FlushMode::kReconfigure) {
// Reset - we'll update it as soon as we have all the information.
batch_state_.needs_reconfigure = true;
}
return true;
}
void DrawBatcher::CopyConstants() {
// TODO(benvanik): partial updates, etc. We could use shader constant access
// knowledge that we get at compile time to only upload those constants
// required. If we did this as a variable length then we could really cut
// down on state block sizes.
std::memcpy(active_draw_.header->float_consts,
&register_file_->values[XE_GPU_REG_SHADER_CONSTANT_000_X].f32,
sizeof(active_draw_.header->float_consts));
std::memcpy(
active_draw_.header->bool_consts,
&register_file_->values[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].f32,
sizeof(active_draw_.header->bool_consts));
std::memcpy(active_draw_.header->loop_consts,
&register_file_->values[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].f32,
sizeof(active_draw_.header->loop_consts));
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@@ -1,191 +0,0 @@
/**
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_DRAW_BATCHER_H_
#define XENIA_GPU_GL4_DRAW_BATCHER_H_
#include "xenia/gpu/gl4/gl4_shader.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/gl/circular_buffer.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace gpu {
namespace gl4 {
using xe::ui::gl::CircularBuffer;
union float4 {
float v[4];
struct {
float x, y, z, w;
};
};
#pragma pack(push, 4)
struct DrawArraysIndirectCommand {
GLuint count;
GLuint instance_count;
GLuint first_index;
GLuint base_instance;
};
struct DrawElementsIndirectCommand {
GLuint count;
GLuint instance_count;
GLuint first_index;
GLint base_vertex;
GLuint base_instance;
};
#pragma pack(pop)
class DrawBatcher {
public:
enum class FlushMode {
kMakeCoherent,
kStateChange,
kReconfigure,
};
explicit DrawBatcher(RegisterFile* register_file);
bool Initialize(CircularBuffer* array_data_buffer);
void Shutdown();
PrimitiveType prim_type() const { return batch_state_.prim_type; }
void set_window_scalar(float width_scalar, float height_scalar) {
active_draw_.header->window_scale.x = width_scalar;
active_draw_.header->window_scale.y = height_scalar;
}
void set_vtx_fmt(float xy, float z, float w) {
active_draw_.header->vtx_fmt.x = xy;
active_draw_.header->vtx_fmt.y = xy;
active_draw_.header->vtx_fmt.z = z;
active_draw_.header->vtx_fmt.w = w;
}
void set_alpha_test(bool enabled, uint32_t func, float ref) {
active_draw_.header->alpha_test.x = enabled ? 1.0f : 0.0f;
active_draw_.header->alpha_test.y = static_cast<float>(func);
active_draw_.header->alpha_test.z = ref;
}
void set_ps_param_gen(int register_index) {
active_draw_.header->ps_param_gen = register_index;
}
void set_texture_sampler(int index, GLuint64 handle, uint32_t swizzle) {
active_draw_.header->texture_samplers[index] = handle;
active_draw_.header->texture_swizzles[index] = swizzle;
}
void set_index_buffer(const CircularBuffer::Allocation& allocation) {
// Offset is used in glDrawElements.
auto& cmd = active_draw_.draw_elements_cmd;
size_t index_size = batch_state_.index_type == GL_UNSIGNED_SHORT ? 2 : 4;
cmd->first_index = GLuint(allocation.offset / index_size);
}
bool ReconfigurePipeline(GL4Shader* vertex_shader, GL4Shader* pixel_shader,
GLuint pipeline);
bool BeginDrawArrays(PrimitiveType prim_type, uint32_t index_count);
bool BeginDrawElements(PrimitiveType prim_type, uint32_t index_count,
IndexFormat index_format);
void DiscardDraw();
bool CommitDraw();
bool Flush(FlushMode mode);
// TFB - Filled with vertex shader output from the last flush.
size_t QueryTFBSize();
bool ReadbackTFB(void* buffer, size_t size);
GLuint tfvbo() { return tfvbo_; }
bool is_tfb_enabled() const { return tfb_enabled_; }
void set_tfb_enabled(bool enabled) { tfb_enabled_ = enabled; }
private:
bool InitializeTFB();
void ShutdownTFB();
void TFBBegin(PrimitiveType prim_type);
void TFBEnd();
bool BeginDraw();
void CopyConstants();
RegisterFile* register_file_;
CircularBuffer command_buffer_;
CircularBuffer state_buffer_;
CircularBuffer* array_data_buffer_;
GLuint tfbo_ = 0;
GLuint tfvbo_ = 0;
GLuint tfqo_ = 0;
PrimitiveType tfb_prim_type_ = PrimitiveType::kNone;
GLenum tfb_prim_type_gl_ = 0;
GLint tfb_prim_count_ = 0;
bool tfb_enabled_ = false;
struct BatchState {
bool needs_reconfigure;
PrimitiveType prim_type;
bool indexed;
GLenum index_type;
GL4Shader* vertex_shader;
GL4Shader* pixel_shader;
GLuint pipeline;
GLsizei command_stride;
GLsizei state_stride;
GLsizei float_consts_offset;
GLsizei bool_consts_offset;
GLsizei loop_consts_offset;
uintptr_t command_range_start;
uintptr_t command_range_length;
uintptr_t state_range_start;
uintptr_t state_range_length;
GLsizei draw_count;
} batch_state_;
// This must match GL4Shader's header.
struct CommonHeader {
float4 window_scale; // sx,sy, ?, ?
float4 vtx_fmt; //
float4 alpha_test; // alpha test enable, func, ref, ?
int ps_param_gen;
int padding[3];
// TODO(benvanik): pack tightly
GLuint64 texture_samplers[32];
GLuint texture_swizzles[32];
float4 float_consts[512];
uint32_t bool_consts[8];
uint32_t loop_consts[32];
};
struct {
CircularBuffer::Allocation command_allocation;
CircularBuffer::Allocation state_allocation;
union {
DrawArraysIndirectCommand* draw_arrays_cmd;
DrawElementsIndirectCommand* draw_elements_cmd;
uintptr_t command_address;
};
CommonHeader* header;
} active_draw_;
bool draw_open_;
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_DRAW_BATCHER_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,237 +0,0 @@
/**
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_
#define XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_
#include <atomic>
#include <cstring>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <unordered_map>
#include <vector>
#include "xenia/base/threading.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/gl4/draw_batcher.h"
#include "xenia/gpu/gl4/gl4_shader.h"
#include "xenia/gpu/gl4/gl4_shader_cache.h"
#include "xenia/gpu/gl4/texture_cache.h"
#include "xenia/gpu/glsl_shader_translator.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/xenos.h"
#include "xenia/kernel/xthread.h"
#include "xenia/memory.h"
#include "xenia/ui/gl/circular_buffer.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace gpu {
namespace gl4 {
class GL4GraphicsSystem;
class GL4CommandProcessor : public CommandProcessor {
public:
GL4CommandProcessor(GL4GraphicsSystem* graphics_system,
kernel::KernelState* kernel_state);
~GL4CommandProcessor() override;
void ClearCaches() override;
// HACK: for debugging; would be good to have this in a base type.
TextureCache* texture_cache() { return &texture_cache_; }
DrawBatcher* draw_batcher() { return &draw_batcher_; }
GLuint GetColorRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base, ColorRenderTargetFormat format);
GLuint GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base, DepthRenderTargetFormat format);
private:
enum class UpdateStatus {
kCompatible,
kMismatch,
kError,
};
struct CachedFramebuffer {
GLuint color_targets[4];
GLuint depth_target;
GLuint framebuffer;
};
struct CachedColorRenderTarget {
uint32_t base;
uint32_t width;
uint32_t height;
ColorRenderTargetFormat format;
GLenum internal_format;
GLuint texture;
};
struct CachedDepthRenderTarget {
uint32_t base;
uint32_t width;
uint32_t height;
DepthRenderTargetFormat format;
GLenum internal_format;
GLuint texture;
};
struct CachedPipeline {
CachedPipeline();
~CachedPipeline();
GLuint vertex_program;
GLuint fragment_program;
struct {
GLuint default_pipeline;
GLuint point_list_pipeline;
GLuint rect_list_pipeline;
GLuint quad_list_pipeline;
GLuint line_quad_list_pipeline;
// TODO(benvanik): others with geometry shaders.
} handles;
};
bool SetupContext() override;
void ShutdownContext() override;
GLuint CreateGeometryProgram(const std::string& source);
void MakeCoherent() override;
void PrepareForWait() override;
void ReturnFromWait() override;
void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) override;
Shader* LoadShader(ShaderType shader_type, uint32_t guest_address,
const uint32_t* host_address,
uint32_t dword_count) override;
bool IssueDraw(PrimitiveType prim_type, uint32_t index_count,
IndexBufferInfo* index_buffer_info) override;
UpdateStatus UpdateShaders(PrimitiveType prim_type);
UpdateStatus UpdateRenderTargets();
UpdateStatus UpdateState(PrimitiveType prim_type);
UpdateStatus UpdateViewportState();
UpdateStatus UpdateRasterizerState(PrimitiveType prim_type);
UpdateStatus UpdateBlendState();
UpdateStatus UpdateDepthStencilState();
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
UpdateStatus PopulateVertexBuffers();
UpdateStatus PopulateSamplers();
UpdateStatus PopulateSampler(const Shader::TextureBinding& texture_binding);
bool IssueCopy() override;
CachedFramebuffer* GetFramebuffer(GLuint color_targets[4],
GLuint depth_target);
GlslShaderTranslator shader_translator_;
GL4ShaderCache shader_cache_;
CachedFramebuffer* active_framebuffer_ = nullptr;
GLuint last_framebuffer_texture_ = 0;
std::vector<CachedFramebuffer> cached_framebuffers_;
std::vector<CachedColorRenderTarget> cached_color_render_targets_;
std::vector<CachedDepthRenderTarget> cached_depth_render_targets_;
std::vector<std::unique_ptr<CachedPipeline>> all_pipelines_;
std::unordered_map<uint64_t, CachedPipeline*> cached_pipelines_;
GLuint point_list_geometry_program_ = 0;
GLuint rect_list_geometry_program_ = 0;
GLuint quad_list_geometry_program_ = 0;
GLuint line_quad_list_geometry_program_ = 0;
TextureCache texture_cache_;
DrawBatcher draw_batcher_;
xe::ui::gl::CircularBuffer scratch_buffer_;
private:
bool SetShadowRegister(uint32_t* dest, uint32_t register_name);
bool SetShadowRegister(float* dest, uint32_t register_name);
struct UpdateRenderTargetsRegisters {
uint32_t rb_modecontrol;
uint32_t rb_surface_info;
uint32_t rb_color_info;
uint32_t rb_color1_info;
uint32_t rb_color2_info;
uint32_t rb_color3_info;
uint32_t rb_color_mask;
uint32_t rb_depthcontrol;
uint32_t rb_stencilrefmask;
uint32_t rb_depth_info;
UpdateRenderTargetsRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_render_targets_regs_;
struct UpdateViewportStateRegisters {
// uint32_t pa_cl_clip_cntl;
uint32_t rb_surface_info;
uint32_t pa_cl_vte_cntl;
uint32_t pa_su_sc_mode_cntl;
uint32_t pa_sc_window_offset;
uint32_t pa_sc_window_scissor_tl;
uint32_t pa_sc_window_scissor_br;
float pa_cl_vport_xoffset;
float pa_cl_vport_yoffset;
float pa_cl_vport_zoffset;
float pa_cl_vport_xscale;
float pa_cl_vport_yscale;
float pa_cl_vport_zscale;
UpdateViewportStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_viewport_state_regs_;
struct UpdateRasterizerStateRegisters {
uint32_t pa_su_sc_mode_cntl;
uint32_t pa_sc_screen_scissor_tl;
uint32_t pa_sc_screen_scissor_br;
uint32_t multi_prim_ib_reset_index;
uint32_t pa_sc_viz_query;
PrimitiveType prim_type;
UpdateRasterizerStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_rasterizer_state_regs_;
struct UpdateBlendStateRegisters {
uint32_t rb_blendcontrol[4];
float rb_blend_rgba[4];
UpdateBlendStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_blend_state_regs_;
struct UpdateDepthStencilStateRegisters {
uint32_t rb_depthcontrol;
uint32_t rb_stencilrefmask;
UpdateDepthStencilStateRegisters() { Reset(); }
void Reset() { std::memset(this, 0, sizeof(*this)); }
} update_depth_stencil_state_regs_;
struct UpdateShadersRegisters {
PrimitiveType prim_type;
uint32_t pa_su_sc_mode_cntl;
uint32_t sq_program_cntl;
uint32_t sq_context_misc;
GL4Shader* vertex_shader;
GL4Shader* pixel_shader;
UpdateShadersRegisters() { Reset(); }
void Reset() {
sq_program_cntl = 0;
vertex_shader = pixel_shader = nullptr;
}
} update_shaders_regs_;
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_GL4_COMMAND_PROCESSOR_H_

View File

@@ -1,17 +0,0 @@
/**
******************************************************************************
* 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_gpu_flags.h"
DEFINE_bool(disable_framebuffer_readback, false,
"Disable framebuffer readback.");
DEFINE_bool(disable_textures, false, "Disable textures and use colors only.");
DEFINE_string(shader_cache_dir, "",
"GL4 Shader cache directory (relative to Xenia). Specify an "
"empty string to disable the cache.");

View File

@@ -1,21 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_GL4_GPU_FLAGS_H_
#define XENIA_GPU_GL4_GL4_GPU_FLAGS_H_
#include <gflags/gflags.h>
DECLARE_bool(disable_framebuffer_readback);
DECLARE_bool(disable_textures);
DECLARE_string(shader_cache_dir);
#define FINE_GRAINED_DRAW_SCOPES 0
#endif // XENIA_GPU_GL4_GL4_GPU_FLAGS_H_

View File

@@ -1,86 +0,0 @@
/**
******************************************************************************
* 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_graphics_system.h"
#include <algorithm>
#include <cstring>
#include "xenia/base/logging.h"
#include "xenia/base/profiling.h"
#include "xenia/cpu/processor.h"
#include "xenia/gpu/gl4/gl4_command_processor.h"
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/ui/gl/gl_provider.h"
#include "xenia/ui/window.h"
namespace xe {
namespace gpu {
namespace gl4 {
GL4GraphicsSystem::GL4GraphicsSystem() = default;
GL4GraphicsSystem::~GL4GraphicsSystem() = default;
X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
kernel::KernelState* kernel_state,
ui::Window* target_window) {
// Must create the provider so we can create contexts.
provider_ = xe::ui::gl::GLProvider::Create(target_window);
auto result = GraphicsSystem::Setup(processor, kernel_state, target_window);
if (result) {
return result;
}
display_context_ =
reinterpret_cast<xe::ui::gl::GLContext*>(target_window->context());
return X_STATUS_SUCCESS;
}
void GL4GraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); }
std::unique_ptr<CommandProcessor> GL4GraphicsSystem::CreateCommandProcessor() {
return std::unique_ptr<CommandProcessor>(
new GL4CommandProcessor(this, kernel_state_));
}
void GL4GraphicsSystem::Swap(xe::ui::UIEvent* e) {
if (!command_processor_) {
return;
}
// Check for pending swap.
auto& swap_state = command_processor_->swap_state();
{
std::lock_guard<std::mutex> lock(swap_state.mutex);
if (swap_state.pending) {
swap_state.pending = false;
std::swap(swap_state.front_buffer_texture,
swap_state.back_buffer_texture);
}
}
if (!swap_state.front_buffer_texture) {
// Not yet ready.
return;
}
// Blit the frontbuffer.
display_context_->blitter()->BlitTexture2D(
static_cast<GLuint>(swap_state.front_buffer_texture),
Rect2D(0, 0, swap_state.width, swap_state.height),
Rect2D(0, 0, target_window_->width(), target_window_->height()),
GL_LINEAR, false);
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@@ -1,45 +0,0 @@
/**
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_GL4_GRAPHICS_SYSTEM_H_
#define XENIA_GPU_GL4_GL4_GRAPHICS_SYSTEM_H_
#include <memory>
#include "xenia/gpu/graphics_system.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace gpu {
namespace gl4 {
class GL4GraphicsSystem : public GraphicsSystem {
public:
GL4GraphicsSystem();
~GL4GraphicsSystem() override;
std::wstring name() const override { return L"GL4"; }
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::Window* target_window) override;
void Shutdown() override;
private:
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
void Swap(xe::ui::UIEvent* e) override;
xe::ui::gl::GLContext* display_context_ = nullptr;
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_GL4_GRAPHICS_SYSTEM_H_

View File

@@ -1,298 +0,0 @@
/**
******************************************************************************
* 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) {}
GL4Shader::~GL4Shader() {
glDeleteProgram(program_);
glDeleteVertexArrays(1, &vao_);
}
bool GL4Shader::Prepare() {
// Build static vertex array descriptor.
if (!PrepareVertexArrayObject()) {
XELOGE("Unable to prepare vertex shader array object");
return false;
}
bool success = true;
if (!CompileShader()) {
host_error_log_ = GetShaderInfoLog();
success = false;
}
if (success && !LinkProgram()) {
host_error_log_ = GetProgramInfoLog();
success = false;
}
if (success) {
host_binary_ = GetBinary();
host_disassembly_ = GetHostDisasmNV(host_binary_);
}
is_valid_ = success;
return success;
}
bool GL4Shader::LoadFromBinary(const uint8_t* blob, GLenum binary_format,
size_t length) {
program_ = glCreateProgram();
glProgramBinary(program_, binary_format, blob, GLsizei(length));
GLint link_status = 0;
glGetProgramiv(program_, GL_LINK_STATUS, &link_status);
if (!link_status) {
// Failed to link. Not fatal - just clean up so we can get generated later.
XELOGD("GL4Shader::LoadFromBinary failed. Log:\n%s",
GetProgramInfoLog().c_str());
glDeleteProgram(program_);
program_ = 0;
return false;
}
// Build static vertex array descriptor.
if (!PrepareVertexArrayObject()) {
XELOGE("Unable to prepare vertex shader array object");
return false;
}
// Success!
host_binary_ = GetBinary();
host_disassembly_ = GetHostDisasmNV(host_binary_);
is_valid_ = true;
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 = 0;
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 : GL_UNSIGNED_INT;
comp_count = 1;
break;
case VertexFormat::k_10_11_11:
comp_type = is_signed ? GL_INT : GL_UNSIGNED_INT;
comp_count = 1;
break;
case VertexFormat::k_11_11_10:
assert_true(is_signed);
comp_type = is_signed ? GL_R11F_G11F_B10F : 0;
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::CompileShader() {
assert_zero(program_);
shader_ =
glCreateShader(shader_type_ == ShaderType::kVertex ? GL_VERTEX_SHADER
: GL_FRAGMENT_SHADER);
if (!shader_) {
XELOGE("OpenGL could not create a shader object!");
return false;
}
auto source_str = GetTranslatedBinaryString();
auto source_str_ptr = source_str.c_str();
GLint source_length = GLint(source_str.length());
glShaderSource(shader_, 1, &source_str_ptr, &source_length);
glCompileShader(shader_);
GLint status = 0;
glGetShaderiv(shader_, GL_COMPILE_STATUS, &status);
return status == GL_TRUE;
}
bool GL4Shader::LinkProgram() {
program_ = glCreateProgram();
if (!program_) {
XELOGE("OpenGL could not create a shader program!");
return false;
}
glAttachShader(program_, shader_);
// Enable TFB
if (shader_type_ == ShaderType::kVertex) {
const GLchar* feedbackVaryings = "gl_Position";
glTransformFeedbackVaryings(program_, 1, &feedbackVaryings,
GL_SEPARATE_ATTRIBS);
}
glProgramParameteri(program_, GL_PROGRAM_SEPARABLE, GL_TRUE);
glLinkProgram(program_);
GLint link_status = 0;
glGetProgramiv(program_, GL_LINK_STATUS, &link_status);
if (!link_status) {
assert_always("Unable to link generated shader");
return false;
}
return true;
}
std::string GL4Shader::GetShaderInfoLog() {
if (!shader_) {
return "GL4Shader::GetShaderInfoLog(): Program is NULL";
}
std::string log;
GLint log_length = 0;
glGetShaderiv(shader_, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
log.resize(log_length - 1);
glGetShaderInfoLog(shader_, log_length, &log_length, &log[0]);
}
return log;
}
std::string GL4Shader::GetProgramInfoLog() {
if (!program_) {
return "GL4Shader::GetProgramInfoLog(): Program is NULL";
}
std::string log;
GLint log_length = 0;
glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
log.resize(log_length - 1);
glGetProgramInfoLog(program_, log_length, &log_length, &log[0]);
}
return log;
}
std::vector<uint8_t> GL4Shader::GetBinary(GLenum* binary_format) {
std::vector<uint8_t> binary;
// Get program binary, if it's available.
GLint binary_length = 0;
glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &binary_length);
if (binary_length) {
binary.resize(binary_length);
GLenum binary_format_tmp = 0;
glGetProgramBinary(program_, binary_length, &binary_length,
&binary_format_tmp, binary.data());
if (binary_format) {
*binary_format = binary_format_tmp;
}
}
return binary;
}
std::string GL4Shader::GetHostDisasmNV(const std::vector<uint8_t>& binary) {
// 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.
std::string disasm;
const char* disasm_start = nullptr;
size_t search_offset = 0;
const char* search_start = reinterpret_cast<const char*>(binary.data());
while (true) {
auto p = reinterpret_cast<const char*>(memchr(
binary.data() + search_offset, '!', 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) {
disasm = std::string(disasm_start);
} else {
disasm = std::string("Shader disassembly not available.");
}
return disasm;
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@@ -1,54 +0,0 @@
/**
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_GL4_SHADER_H_
#define XENIA_GPU_GL4_GL4_SHADER_H_
#include <string>
#include "xenia/gpu/shader.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace gpu {
namespace gl4 {
class GL4Shader : public Shader {
public:
GL4Shader(ShaderType shader_type, uint64_t data_hash,
const uint32_t* dword_ptr, uint32_t dword_count);
~GL4Shader() override;
GLuint program() const { return program_; }
GLuint shader() const { return shader_; }
GLuint vao() const { return vao_; }
bool Prepare();
bool LoadFromBinary(const uint8_t* blob, GLenum binary_format, size_t length);
std::vector<uint8_t> GetBinary(GLenum* binary_format = nullptr);
protected:
bool PrepareVertexArrayObject();
bool CompileShader();
bool LinkProgram();
std::string GetShaderInfoLog();
std::string GetProgramInfoLog();
static std::string GetHostDisasmNV(const std::vector<uint8_t>& binary);
GLuint program_ = 0;
GLuint shader_ = 0;
GLuint vao_ = 0;
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_GL4_SHADER_H_

View File

@@ -1,187 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/gl4/gl4_shader_cache.h"
#include <cinttypes>
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/mapped_memory.h"
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
#include "xenia/gpu/gl4/gl4_shader.h"
#include "xenia/gpu/glsl_shader_translator.h"
#include "xenia/gpu/gpu_flags.h"
#include "third_party/xxhash/xxhash.h"
namespace xe {
namespace gpu {
namespace gl4 {
GL4ShaderCache::GL4ShaderCache(GlslShaderTranslator* shader_translator)
: shader_translator_(shader_translator) {}
GL4ShaderCache::~GL4ShaderCache() {}
void GL4ShaderCache::Reset() {
shader_map_.clear();
all_shaders_.clear();
}
GL4Shader* GL4ShaderCache::LookupOrInsertShader(ShaderType shader_type,
const uint32_t* dwords,
uint32_t dword_count) {
// Hash the input memory and lookup the shader.
GL4Shader* shader_ptr = nullptr;
uint64_t hash = XXH64(dwords, dword_count * sizeof(uint32_t), 0);
auto it = shader_map_.find(hash);
if (it != shader_map_.end()) {
// Shader has been previously loaded.
// TODO(benvanik): compare bytes? Likelihood of collision is low.
shader_ptr = it->second;
} else {
// Check filesystem cache.
shader_ptr = FindCachedShader(shader_type, hash, dwords, dword_count);
if (shader_ptr) {
// Found!
XELOGGPU("Loaded %s shader from cache (hash: %.16" PRIX64 ")",
shader_type == ShaderType::kVertex ? "vertex" : "pixel", hash);
return shader_ptr;
}
// Not found in cache - load from scratch.
auto shader =
std::make_unique<GL4Shader>(shader_type, hash, dwords, dword_count);
shader_ptr = shader.get();
shader_map_.insert({hash, shader_ptr});
all_shaders_.emplace_back(std::move(shader));
// Perform translation.
// If this fails the shader will be marked as invalid and ignored later.
if (shader_translator_->Translate(shader_ptr)) {
shader_ptr->Prepare();
if (shader_ptr->is_valid()) {
CacheShader(shader_ptr);
XELOGGPU("Generated %s shader at 0x%.16" PRIX64 " (%db):\n%s",
shader_type == ShaderType::kVertex ? "vertex" : "pixel",
dwords, dword_count * 4,
shader_ptr->ucode_disassembly().c_str());
}
// Dump shader files if desired.
if (!FLAGS_dump_shaders.empty()) {
shader_ptr->Dump(FLAGS_dump_shaders, "gl4");
}
} else {
XELOGE("Shader failed translation");
}
}
return shader_ptr;
}
void GL4ShaderCache::CacheShader(GL4Shader* shader) {
if (FLAGS_shader_cache_dir.empty()) {
// Cache disabled.
return;
}
GLenum binary_format = 0;
auto binary = shader->GetBinary(&binary_format);
if (binary.size() == 0) {
// No binary returned.
return;
}
auto cache_dir = xe::to_absolute_path(xe::to_wstring(FLAGS_shader_cache_dir));
xe::filesystem::CreateFolder(cache_dir);
auto filename =
cache_dir + xe::format_string(
L"%.16" PRIX64 ".%s", shader->ucode_data_hash(),
shader->type() == ShaderType::kPixel ? L"frag" : L"vert");
auto file = xe::filesystem::OpenFile(filename, "wb");
if (!file) {
// Not fatal, but not too good.
return;
}
std::vector<uint8_t> cached_shader_mem;
// Resize this vector to the final filesize (- 1 to account for dummy array
// in CachedShader)
cached_shader_mem.resize(sizeof(CachedShader) + binary.size() - 1);
auto cached_shader =
reinterpret_cast<CachedShader*>(cached_shader_mem.data());
cached_shader->magic = xe::byte_swap('XSHD');
cached_shader->version = 0; // TODO
cached_shader->shader_type = uint8_t(shader->type());
cached_shader->binary_len = uint32_t(binary.size());
cached_shader->binary_format = binary_format;
std::memcpy(cached_shader->binary, binary.data(), binary.size());
fwrite(cached_shader_mem.data(), cached_shader_mem.size(), 1, file);
fclose(file);
}
GL4Shader* GL4ShaderCache::FindCachedShader(ShaderType shader_type,
uint64_t hash,
const uint32_t* dwords,
uint32_t dword_count) {
if (FLAGS_shader_cache_dir.empty()) {
// Cache disabled.
return nullptr;
}
auto cache_dir = xe::to_absolute_path(xe::to_wstring(FLAGS_shader_cache_dir));
auto filename =
cache_dir +
xe::format_string(L"%.16" PRIX64 ".%s", hash,
shader_type == ShaderType::kPixel ? L"frag" : L"vert");
if (!xe::filesystem::PathExists(filename)) {
return nullptr;
}
// Shader is cached. Open it up.
auto map = xe::MappedMemory::Open(filename, MappedMemory::Mode::kRead);
if (!map) {
// Should not fail
assert_always();
return nullptr;
}
auto cached_shader = reinterpret_cast<CachedShader*>(map->data());
// TODO: Compare versions
if (cached_shader->magic != xe::byte_swap('XSHD')) {
return nullptr;
}
auto shader =
std::make_unique<GL4Shader>(shader_type, hash, dwords, dword_count);
// Gather the binding points.
// TODO: Make Shader do this on construction.
// TODO: Regenerate microcode disasm/etc on load.
shader_translator_->GatherAllBindingInformation(shader.get());
if (!shader->LoadFromBinary(cached_shader->binary,
cached_shader->binary_format,
cached_shader->binary_len)) {
// Failed to load from binary.
return nullptr;
}
auto shader_ptr = shader.get();
shader_map_.insert({hash, shader_ptr});
all_shaders_.emplace_back(std::move(shader));
return shader_ptr;
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@@ -1,62 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_SHADER_CACHE_H_
#define XENIA_GPU_GL4_SHADER_CACHE_H_
#include <cstdint>
#include <cstring>
#include <memory>
#include <unordered_map>
#include <vector>
#include "xenia/gpu/xenos.h"
namespace xe {
namespace gpu {
class GlslShaderTranslator;
namespace gl4 {
class GL4Shader;
class GL4ShaderCache {
public:
GL4ShaderCache(GlslShaderTranslator* shader_translator);
~GL4ShaderCache();
void Reset();
GL4Shader* LookupOrInsertShader(ShaderType shader_type,
const uint32_t* dwords, uint32_t dword_count);
private:
// Cached shader file format.
struct CachedShader {
uint32_t magic;
uint32_t version; // Version of the shader translator used.
uint8_t shader_type; // ShaderType enum
uint32_t binary_len; // Code length
uint32_t binary_format; // Binary format (from OpenGL)
uint8_t binary[1]; // Code
};
void CacheShader(GL4Shader* shader);
GL4Shader* FindCachedShader(ShaderType shader_type, uint64_t hash,
const uint32_t* dwords, uint32_t dword_count);
GlslShaderTranslator* shader_translator_ = nullptr;
std::vector<std::unique_ptr<GL4Shader>> all_shaders_;
std::unordered_map<uint64_t, GL4Shader*> shader_map_;
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_SHADER_CACHE_H_

View File

@@ -1,109 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/gpu/gl4/gl4_command_processor.h"
#include "xenia/gpu/gl4/gl4_graphics_system.h"
#include "xenia/gpu/trace_viewer.h"
namespace xe {
namespace gpu {
namespace gl4 {
using namespace xe::gpu::xenos;
class GL4TraceViewer : public TraceViewer {
public:
std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() override {
return std::unique_ptr<gpu::GraphicsSystem>(new GL4GraphicsSystem());
}
uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base,
ColorRenderTargetFormat format) override {
auto command_processor = static_cast<GL4CommandProcessor*>(
graphics_system_->command_processor());
return command_processor->GetColorRenderTarget(pitch, samples, base,
format);
}
uintptr_t GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples,
uint32_t base,
DepthRenderTargetFormat format) override {
auto command_processor = static_cast<GL4CommandProcessor*>(
graphics_system_->command_processor());
return command_processor->GetDepthRenderTarget(pitch, samples, base,
format);
}
uintptr_t GetTextureEntry(const TextureInfo& texture_info,
const SamplerInfo& sampler_info) override {
auto command_processor = static_cast<GL4CommandProcessor*>(
graphics_system_->command_processor());
auto entry_view =
command_processor->texture_cache()->Demand(texture_info, sampler_info);
if (!entry_view) {
return 0;
}
auto texture = entry_view->texture;
return static_cast<uintptr_t>(texture->handle);
}
size_t QueryVSOutputSize() override {
auto command_processor = static_cast<GL4CommandProcessor*>(
graphics_system_->command_processor());
auto draw_batcher = command_processor->draw_batcher();
return draw_batcher->QueryTFBSize();
}
size_t QueryVSOutputElementSize() override {
// vec4 always has 4 elements.
return 4;
}
bool QueryVSOutput(void* buffer, size_t size) override {
auto command_processor = static_cast<GL4CommandProcessor*>(
graphics_system_->command_processor());
auto draw_batcher = command_processor->draw_batcher();
return draw_batcher->ReadbackTFB(buffer, size);
}
bool Setup() override {
if (!TraceViewer::Setup()) {
return false;
}
// Enable TFB
auto command_processor = static_cast<GL4CommandProcessor*>(
graphics_system_->command_processor());
auto draw_batcher = command_processor->draw_batcher();
draw_batcher->set_tfb_enabled(true);
return true;
}
private:
};
int trace_viewer_main(const std::vector<std::wstring>& args) {
GL4TraceViewer trace_viewer;
return trace_viewer.Main(args);
}
} // namespace gl4
} // namespace gpu
} // namespace xe
DEFINE_ENTRY_POINT(L"xenia-gpu-gl4-trace-viewer",
L"xenia-gpu-gl4-trace-viewer some.trace",
xe::gpu::gl4::trace_viewer_main);

View File

@@ -1,97 +0,0 @@
project_root = "../../../.."
include(project_root.."/tools/build")
group("src")
project("xenia-gpu-gl4")
uuid("da10149d-efb0-44aa-924c-a76a46e1f04d")
kind("StaticLib")
language("C++")
links({
"glew",
"xenia-base",
"xenia-gpu",
"xenia-ui",
"xenia-ui-gl",
"xxhash",
})
defines({
"GLEW_STATIC=1",
"GLEW_MX=1",
})
includedirs({
project_root.."/third_party/gflags/src",
})
local_platform_files()
-- TODO(benvanik): kill this and move to the debugger UI.
group("src")
project("xenia-gpu-gl4-trace-viewer")
uuid("450f965b-a019-4ba5-bc6f-99901e5a4c8d")
kind("WindowedApp")
language("C++")
links({
"capstone",
"gflags",
"glew",
"imgui",
"libavcodec",
"libavutil",
"snappy",
"xenia-apu",
"xenia-apu-nop",
"xenia-base",
"xenia-core",
"xenia-cpu",
"xenia-cpu-backend-x64",
"xenia-gpu",
"xenia-gpu-gl4",
"xenia-hid",
"xenia-hid-nop",
"xenia-kernel",
"xenia-ui",
"xenia-ui-gl",
"xenia-vfs",
"xxhash",
})
flags({
"WinMain", -- Use WinMain instead of main.
})
defines({
"GLEW_STATIC=1",
"GLEW_MX=1",
})
includedirs({
project_root.."/third_party/gflags/src",
})
files({
"gl4_trace_viewer_main.cc",
"../../base/main_"..platform_suffix..".cc",
})
filter("platforms:Linux")
links({
"X11",
"xcb",
"X11-xcb",
"GL",
"vulkan",
})
filter("platforms:Windows")
links({
"xenia-apu-xaudio2",
"xenia-hid-winkey",
"xenia-hid-xinput",
})
-- Only create the .user file if it doesn't already exist.
local user_file = project_root.."/build/xenia-gpu-gl4-trace-viewer.vcxproj.user"
if not os.isfile(user_file) then
debugdir(project_root)
debugargs({
"--flagfile=scratch/flags.txt",
"2>&1",
"1>scratch/stdout-trace-viewer.txt",
})
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +0,0 @@
/**
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_TEXTURE_CACHE_H_
#define XENIA_GPU_GL4_TEXTURE_CACHE_H_
#include <mutex>
#include <unordered_map>
#include <vector>
#include "xenia/gpu/sampler_info.h"
#include "xenia/gpu/texture_info.h"
#include "xenia/memory.h"
#include "xenia/ui/gl/blitter.h"
#include "xenia/ui/gl/circular_buffer.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace gpu {
namespace gl4 {
using xe::ui::gl::Blitter;
using xe::ui::gl::CircularBuffer;
using xe::ui::gl::Rect2D;
class TextureCache {
public:
struct TextureEntry;
struct SamplerEntry {
SamplerInfo sampler_info;
GLuint handle;
};
struct TextureEntryView {
TextureEntry* texture;
SamplerEntry* sampler;
uint64_t sampler_hash;
GLuint64 texture_sampler_handle;
};
struct TextureEntry {
TextureInfo texture_info;
uintptr_t access_watch_handle;
GLuint handle;
bool pending_invalidation;
std::vector<std::unique_ptr<TextureEntryView>> views;
};
TextureCache();
~TextureCache();
bool Initialize(Memory* memory, CircularBuffer* scratch_buffer);
void Shutdown();
void Scavenge();
void Clear();
void EvictAllTextures();
TextureEntryView* Demand(const TextureInfo& texture_info,
const SamplerInfo& sampler_info);
GLuint CopyTexture(Blitter* blitter, uint32_t guest_address,
uint32_t logical_width, uint32_t logical_height,
uint32_t block_width, uint32_t block_height,
TextureFormat format, bool swap_channels,
GLuint src_texture, Rect2D src_rect, Rect2D dest_rect);
GLuint ConvertTexture(Blitter* blitter, uint32_t guest_address,
uint32_t logical_width, uint32_t logical_height,
uint32_t block_width, uint32_t block_height,
TextureFormat format, bool swap_channels,
GLuint src_texture, Rect2D src_rect, Rect2D dest_rect);
TextureEntry* LookupAddress(uint32_t guest_address, uint32_t width,
uint32_t height, TextureFormat format);
private:
struct ReadBufferTexture {
uintptr_t access_watch_handle;
uint32_t guest_address;
uint32_t logical_width;
uint32_t logical_height;
uint32_t block_width;
uint32_t block_height;
TextureFormat format;
GLuint handle;
};
SamplerEntry* LookupOrInsertSampler(const SamplerInfo& sampler_info,
uint64_t opt_hash = 0);
void EvictSampler(SamplerEntry* entry);
TextureEntry* LookupOrInsertTexture(const TextureInfo& texture_info,
uint64_t opt_hash = 0);
void EvictTexture(TextureEntry* entry);
bool UploadTexture1D(GLuint texture, const TextureInfo& texture_info);
bool UploadTexture2D(GLuint texture, const TextureInfo& texture_info);
bool UploadTextureCube(GLuint texture, const TextureInfo& texture_info);
Memory* memory_;
CircularBuffer* scratch_buffer_;
std::unordered_map<uint64_t, SamplerEntry*> sampler_entries_;
std::unordered_map<uint64_t, TextureEntry*> texture_entries_;
std::vector<ReadBufferTexture*> read_buffer_textures_;
std::mutex invalidated_textures_mutex_;
std::vector<TextureEntry*>* invalidated_textures_;
std::vector<TextureEntry*> invalidated_textures_sets_[2];
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_TEXTURE_CACHE_H_