[GL4] Farewell, GL4 backend
This commit is contained in:
parent
f54ac240a4
commit
040d8d1ade
|
@ -242,13 +242,11 @@ solution("xenia")
|
|||
include("src/xenia/debug/ui")
|
||||
include("src/xenia/gpu")
|
||||
include("src/xenia/gpu/null")
|
||||
include("src/xenia/gpu/gl4")
|
||||
include("src/xenia/gpu/vulkan")
|
||||
include("src/xenia/hid")
|
||||
include("src/xenia/hid/nop")
|
||||
include("src/xenia/kernel")
|
||||
include("src/xenia/ui")
|
||||
include("src/xenia/ui/gl")
|
||||
include("src/xenia/ui/spirv")
|
||||
include("src/xenia/ui/vulkan")
|
||||
include("src/xenia/vfs")
|
||||
|
|
|
@ -26,14 +26,12 @@ project("xenia-app")
|
|||
"xenia-cpu-backend-x64",
|
||||
"xenia-debug-ui",
|
||||
"xenia-gpu",
|
||||
"xenia-gpu-gl4",
|
||||
"xenia-gpu-null",
|
||||
"xenia-gpu-vulkan",
|
||||
"xenia-hid",
|
||||
"xenia-hid-nop",
|
||||
"xenia-kernel",
|
||||
"xenia-ui",
|
||||
"xenia-ui-gl",
|
||||
"xenia-ui-spirv",
|
||||
"xenia-ui-vulkan",
|
||||
"xenia-vfs",
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#endif // XE_PLATFORM_WIN32
|
||||
|
||||
// Available graphics systems:
|
||||
#include "xenia/gpu/gl4/gl4_graphics_system.h"
|
||||
#include "xenia/gpu/null/null_graphics_system.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
|
||||
|
||||
|
@ -38,7 +37,7 @@
|
|||
#endif // XE_PLATFORM_WIN32
|
||||
|
||||
DEFINE_string(apu, "any", "Audio system. Use: [any, nop, xaudio2]");
|
||||
DEFINE_string(gpu, "any", "Graphics system. Use: [any, gl4, vulkan, null]");
|
||||
DEFINE_string(gpu, "any", "Graphics system. Use: [any, vulkan, null]");
|
||||
DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]");
|
||||
|
||||
DEFINE_string(target, "", "Specifies the target .xex or .iso to execute.");
|
||||
|
@ -71,10 +70,7 @@ std::unique_ptr<apu::AudioSystem> CreateAudioSystem(cpu::Processor* processor) {
|
|||
}
|
||||
|
||||
std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() {
|
||||
if (FLAGS_gpu.compare("gl4") == 0) {
|
||||
return std::unique_ptr<gpu::GraphicsSystem>(
|
||||
new xe::gpu::gl4::GL4GraphicsSystem());
|
||||
} else if (FLAGS_gpu.compare("vulkan") == 0) {
|
||||
if (FLAGS_gpu.compare("vulkan") == 0) {
|
||||
return std::unique_ptr<gpu::GraphicsSystem>(
|
||||
new xe::gpu::vulkan::VulkanGraphicsSystem());
|
||||
} else if (FLAGS_gpu.compare("null") == 0) {
|
||||
|
|
|
@ -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,
|
||||
®ister_file_->values[XE_GPU_REG_SHADER_CONSTANT_000_X].f32,
|
||||
sizeof(active_draw_.header->float_consts));
|
||||
std::memcpy(
|
||||
active_draw_.header->bool_consts,
|
||||
®ister_file_->values[XE_GPU_REG_SHADER_CONSTANT_BOOL_000_031].f32,
|
||||
sizeof(active_draw_.header->bool_consts));
|
||||
std::memcpy(active_draw_.header->loop_consts,
|
||||
®ister_file_->values[XE_GPU_REG_SHADER_CONSTANT_LOOP_00].f32,
|
||||
sizeof(active_draw_.header->loop_consts));
|
||||
}
|
||||
|
||||
} // namespace gl4
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
|
@ -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
|
@ -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_
|
|
@ -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.");
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
|
@ -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
|
@ -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_
|
|
@ -30,7 +30,6 @@ project("xenia-hid-demo")
|
|||
"xenia-hid",
|
||||
"xenia-hid-nop",
|
||||
"xenia-ui",
|
||||
"xenia-ui-gl",
|
||||
})
|
||||
filter("platforms:Linux")
|
||||
links({
|
||||
|
|
|
@ -1,315 +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/ui/gl/blitter.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/gl/gl_context.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
Blitter::Blitter()
|
||||
: vertex_program_(0),
|
||||
color_fragment_program_(0),
|
||||
depth_fragment_program_(0),
|
||||
color_pipeline_(0),
|
||||
depth_pipeline_(0),
|
||||
vbo_(0),
|
||||
vao_(0),
|
||||
nearest_sampler_(0),
|
||||
linear_sampler_(0),
|
||||
scratch_framebuffer_(0) {}
|
||||
|
||||
Blitter::~Blitter() = default;
|
||||
|
||||
bool Blitter::Initialize() {
|
||||
const std::string header =
|
||||
R"(
|
||||
#version 450
|
||||
#extension GL_ARB_explicit_uniform_location : require
|
||||
#extension GL_ARB_shading_language_420pack : require
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
layout(std140, column_major) uniform;
|
||||
layout(std430, column_major) buffer;
|
||||
)";
|
||||
const std::string vs_source = header +
|
||||
R"(
|
||||
layout(location = 0) uniform vec4 src_uv;
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
float gl_PointSize;
|
||||
float gl_ClipDistance[];
|
||||
};
|
||||
layout(location = 0) in vec2 vfetch_pos;
|
||||
layout(location = 0) out vec2 vtx_uv;
|
||||
void main() {
|
||||
gl_Position = vec4(vfetch_pos.xy * vec2(2.0, -2.0) -
|
||||
vec2(1.0, -1.0), 0.0, 1.0);
|
||||
vtx_uv = vfetch_pos.xy * src_uv.zw + src_uv.xy;
|
||||
})";
|
||||
const std::string color_fs_source = header +
|
||||
R"(
|
||||
layout(location = 1) uniform sampler2D src_texture;
|
||||
layout(location = 2) uniform bool swap;
|
||||
layout(location = 0) in vec2 vtx_uv;
|
||||
layout(location = 0) out vec4 oC;
|
||||
void main() {
|
||||
oC = texture(src_texture, vtx_uv);
|
||||
if (!swap) oC = oC.bgra;
|
||||
})";
|
||||
const std::string depth_fs_source = header +
|
||||
R"(
|
||||
layout(location = 1) uniform sampler2D src_texture;
|
||||
layout(location = 0) in vec2 vtx_uv;
|
||||
layout(location = 0) out vec4 oC;
|
||||
void main() {
|
||||
gl_FragDepth = texture(src_texture, vtx_uv).r;
|
||||
})";
|
||||
|
||||
auto vs_source_str = vs_source.c_str();
|
||||
vertex_program_ = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vs_source_str);
|
||||
auto color_fs_source_str = color_fs_source.c_str();
|
||||
color_fragment_program_ =
|
||||
glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &color_fs_source_str);
|
||||
auto depth_fs_source_str = depth_fs_source.c_str();
|
||||
depth_fragment_program_ =
|
||||
glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &depth_fs_source_str);
|
||||
glCreateProgramPipelines(1, &color_pipeline_);
|
||||
glUseProgramStages(color_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
|
||||
glUseProgramStages(color_pipeline_, GL_FRAGMENT_SHADER_BIT,
|
||||
color_fragment_program_);
|
||||
glCreateProgramPipelines(1, &depth_pipeline_);
|
||||
glUseProgramStages(depth_pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
|
||||
glUseProgramStages(depth_pipeline_, GL_FRAGMENT_SHADER_BIT,
|
||||
depth_fragment_program_);
|
||||
|
||||
glCreateBuffers(1, &vbo_);
|
||||
static const GLfloat vbo_data[] = {
|
||||
0, 0, 1, 0, 0, 1, 1, 1,
|
||||
};
|
||||
glNamedBufferStorage(vbo_, sizeof(vbo_data), vbo_data, 0);
|
||||
|
||||
glCreateVertexArrays(1, &vao_);
|
||||
glEnableVertexArrayAttrib(vao_, 0);
|
||||
glVertexArrayAttribBinding(vao_, 0, 0);
|
||||
glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE, 0);
|
||||
glVertexArrayVertexBuffer(vao_, 0, vbo_, 0, sizeof(GLfloat) * 2);
|
||||
|
||||
glCreateSamplers(1, &nearest_sampler_);
|
||||
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glCreateSamplers(1, &linear_sampler_);
|
||||
glSamplerParameteri(linear_sampler_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(linear_sampler_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glCreateFramebuffers(1, &scratch_framebuffer_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Blitter::Shutdown() {
|
||||
glDeleteFramebuffers(1, &scratch_framebuffer_);
|
||||
glDeleteProgram(vertex_program_);
|
||||
glDeleteProgram(color_fragment_program_);
|
||||
glDeleteProgram(depth_fragment_program_);
|
||||
glDeleteProgramPipelines(1, &color_pipeline_);
|
||||
glDeleteProgramPipelines(1, &depth_pipeline_);
|
||||
glDeleteBuffers(1, &vbo_);
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
glDeleteSamplers(1, &nearest_sampler_);
|
||||
glDeleteSamplers(1, &linear_sampler_);
|
||||
}
|
||||
|
||||
struct SavedState {
|
||||
GLboolean scissor_test_enabled;
|
||||
GLboolean depth_test_enabled;
|
||||
GLboolean depth_mask_enabled;
|
||||
GLint depth_func;
|
||||
GLboolean stencil_test_enabled;
|
||||
GLboolean cull_face_enabled;
|
||||
GLint cull_face;
|
||||
GLint front_face;
|
||||
GLint polygon_mode;
|
||||
GLboolean color_mask_0_enabled[4];
|
||||
GLboolean blend_0_enabled;
|
||||
GLint draw_buffer;
|
||||
GLfloat viewport[4];
|
||||
GLint program_pipeline;
|
||||
GLint vertex_array;
|
||||
GLint texture_0;
|
||||
GLint sampler_0;
|
||||
|
||||
void Save() {
|
||||
scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST);
|
||||
depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
|
||||
glGetBooleanv(GL_DEPTH_WRITEMASK, &depth_mask_enabled);
|
||||
glGetIntegerv(GL_DEPTH_FUNC, &depth_func);
|
||||
stencil_test_enabled = glIsEnabled(GL_STENCIL_TEST);
|
||||
cull_face_enabled = glIsEnabled(GL_CULL_FACE);
|
||||
glGetIntegerv(GL_CULL_FACE_MODE, &cull_face);
|
||||
glGetIntegerv(GL_FRONT_FACE, &front_face);
|
||||
glGetIntegerv(GL_POLYGON_MODE, &polygon_mode);
|
||||
glGetBooleani_v(GL_COLOR_WRITEMASK, 0,
|
||||
reinterpret_cast<GLboolean*>(&color_mask_0_enabled));
|
||||
blend_0_enabled = glIsEnabledi(GL_BLEND, 0);
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_buffer);
|
||||
glGetFloati_v(GL_VIEWPORT, 0, viewport);
|
||||
glGetIntegerv(GL_PROGRAM_PIPELINE_BINDING, &program_pipeline);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vertex_array);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture_0);
|
||||
glGetIntegerv(GL_SAMPLER_BINDING, &sampler_0);
|
||||
}
|
||||
|
||||
void Restore() {
|
||||
scissor_test_enabled ? glEnable(GL_SCISSOR_TEST)
|
||||
: glDisable(GL_SCISSOR_TEST);
|
||||
depth_test_enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(depth_mask_enabled);
|
||||
glDepthFunc(depth_func);
|
||||
stencil_test_enabled ? glEnable(GL_STENCIL_TEST)
|
||||
: glDisable(GL_STENCIL_TEST);
|
||||
cull_face_enabled ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
|
||||
glCullFace(cull_face);
|
||||
glFrontFace(front_face);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, polygon_mode);
|
||||
glColorMaski(0, color_mask_0_enabled[0], color_mask_0_enabled[1],
|
||||
color_mask_0_enabled[2], color_mask_0_enabled[3]);
|
||||
blend_0_enabled ? glEnablei(GL_BLEND, 0) : glDisablei(GL_BLEND, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_buffer);
|
||||
glViewportIndexedf(0, viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
glBindProgramPipeline(program_pipeline);
|
||||
glBindVertexArray(vertex_array);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_0);
|
||||
glBindSampler(0, sampler_0);
|
||||
}
|
||||
};
|
||||
|
||||
void Blitter::Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect,
|
||||
GLenum filter) {
|
||||
assert_not_zero(src_texture);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisablei(GL_BLEND, 0);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CW);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glBindVertexArray(vao_);
|
||||
glBindTextures(0, 1, &src_texture);
|
||||
switch (filter) {
|
||||
default:
|
||||
case GL_NEAREST:
|
||||
glBindSampler(0, nearest_sampler_);
|
||||
break;
|
||||
case GL_LINEAR:
|
||||
glBindSampler(0, linear_sampler_);
|
||||
break;
|
||||
}
|
||||
|
||||
glViewportIndexedf(0, GLfloat(dest_rect.x), GLfloat(dest_rect.y),
|
||||
GLfloat(dest_rect.width), GLfloat(dest_rect.height));
|
||||
|
||||
// TODO(benvanik): avoid this?
|
||||
GLint src_texture_width;
|
||||
glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_WIDTH,
|
||||
&src_texture_width);
|
||||
GLint src_texture_height;
|
||||
glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_HEIGHT,
|
||||
&src_texture_height);
|
||||
glProgramUniform4f(vertex_program_, 0,
|
||||
src_rect.x / static_cast<float>(src_texture_width),
|
||||
src_rect.y / static_cast<float>(src_texture_height),
|
||||
src_rect.width / static_cast<float>(src_texture_width),
|
||||
src_rect.height / static_cast<float>(src_texture_height));
|
||||
|
||||
// Useful for seeing the entire framebuffer/etc:
|
||||
// glProgramUniform4f(vertex_program_, 0, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void Blitter::BlitTexture2D(GLuint src_texture, Rect2D src_rect,
|
||||
Rect2D dest_rect, GLenum filter,
|
||||
bool swap_channels) {
|
||||
SavedState state;
|
||||
state.Save();
|
||||
|
||||
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glStencilMask(0xFF);
|
||||
glBindProgramPipeline(color_pipeline_);
|
||||
|
||||
glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0);
|
||||
|
||||
Draw(src_texture, src_rect, dest_rect, filter);
|
||||
|
||||
state.Restore();
|
||||
}
|
||||
|
||||
void Blitter::CopyColorTexture2D(GLuint src_texture, Rect2D src_rect,
|
||||
GLuint dest_texture, Rect2D dest_rect,
|
||||
GLenum filter, bool swap_channels) {
|
||||
SavedState state;
|
||||
state.Save();
|
||||
|
||||
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBindProgramPipeline(color_pipeline_);
|
||||
|
||||
glProgramUniform1i(color_fragment_program_, 2, swap_channels ? 1 : 0);
|
||||
|
||||
glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0,
|
||||
dest_texture, 0);
|
||||
glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_COLOR_ATTACHMENT0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_);
|
||||
Draw(src_texture, src_rect, dest_rect, filter);
|
||||
glNamedFramebufferDrawBuffer(scratch_framebuffer_, GL_NONE);
|
||||
glNamedFramebufferTexture(scratch_framebuffer_, GL_COLOR_ATTACHMENT0, GL_NONE,
|
||||
0);
|
||||
|
||||
state.Restore();
|
||||
}
|
||||
|
||||
void Blitter::CopyDepthTexture(GLuint src_texture, Rect2D src_rect,
|
||||
GLuint dest_texture, Rect2D dest_rect) {
|
||||
SavedState state;
|
||||
state.Save();
|
||||
|
||||
glColorMaski(0, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDepthMask(GL_TRUE);
|
||||
glBindProgramPipeline(depth_pipeline_);
|
||||
|
||||
glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
dest_texture, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, scratch_framebuffer_);
|
||||
Draw(src_texture, src_rect, dest_rect, GL_NEAREST);
|
||||
glNamedFramebufferTexture(scratch_framebuffer_, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_NONE, 0);
|
||||
|
||||
state.Restore();
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,70 +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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_GL_BLITTER_H_
|
||||
#define XENIA_UI_GL_BLITTER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/gl/gl.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
struct Rect2D {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
Rect2D() : x(0), y(0), width(0), height(0) {}
|
||||
Rect2D(int32_t x_, int32_t y_, int32_t width_, int32_t height_)
|
||||
: x(x_), y(y_), width(width_), height(height_) {}
|
||||
int32_t right() const { return x + width; }
|
||||
int32_t bottom() const { return y + height; }
|
||||
};
|
||||
|
||||
class Blitter {
|
||||
public:
|
||||
Blitter();
|
||||
~Blitter();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
void BlitTexture2D(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect,
|
||||
GLenum filter, bool swap_channels);
|
||||
|
||||
void CopyColorTexture2D(GLuint src_texture, Rect2D src_rect,
|
||||
GLuint dest_texture, Rect2D dest_rect, GLenum filter,
|
||||
bool swap_channels);
|
||||
void CopyDepthTexture(GLuint src_texture, Rect2D src_rect,
|
||||
GLuint dest_texture, Rect2D dest_rect);
|
||||
|
||||
private:
|
||||
void Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect,
|
||||
GLenum filter);
|
||||
|
||||
GLuint vertex_program_;
|
||||
GLuint color_fragment_program_;
|
||||
GLuint depth_fragment_program_;
|
||||
GLuint color_pipeline_;
|
||||
GLuint depth_pipeline_;
|
||||
GLuint vbo_;
|
||||
GLuint vao_;
|
||||
GLuint nearest_sampler_;
|
||||
GLuint linear_sampler_;
|
||||
GLuint scratch_framebuffer_;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_BLITTER_H_
|
|
@ -1,139 +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/ui/gl/circular_buffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
CircularBuffer::CircularBuffer(size_t capacity, size_t alignment)
|
||||
: capacity_(capacity),
|
||||
alignment_(alignment),
|
||||
write_head_(0),
|
||||
dirty_start_(UINT64_MAX),
|
||||
dirty_end_(0),
|
||||
buffer_(0),
|
||||
gpu_base_(0),
|
||||
host_base_(nullptr) {}
|
||||
|
||||
CircularBuffer::~CircularBuffer() { Shutdown(); }
|
||||
|
||||
bool CircularBuffer::Initialize() {
|
||||
glCreateBuffers(1, &buffer_);
|
||||
glNamedBufferStorage(buffer_, capacity_, nullptr,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
host_base_ = reinterpret_cast<uint8_t*>(glMapNamedBufferRange(
|
||||
buffer_, 0, capacity_,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_PERSISTENT_BIT));
|
||||
assert_not_null(host_base_);
|
||||
if (!host_base_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CircularBuffer::Shutdown() {
|
||||
if (!buffer_) {
|
||||
return;
|
||||
}
|
||||
glUnmapNamedBuffer(buffer_);
|
||||
glDeleteBuffers(1, &buffer_);
|
||||
buffer_ = 0;
|
||||
}
|
||||
|
||||
bool CircularBuffer::CanAcquire(size_t length) {
|
||||
size_t aligned_length = xe::round_up(length, alignment_);
|
||||
return write_head_ + aligned_length <= capacity_;
|
||||
}
|
||||
|
||||
CircularBuffer::Allocation CircularBuffer::Acquire(size_t length) {
|
||||
// Addresses must always be % 256.
|
||||
size_t aligned_length = xe::round_up(length, alignment_);
|
||||
assert_true(aligned_length <= capacity_, "Request too large");
|
||||
if (write_head_ + aligned_length > capacity_) {
|
||||
// Flush and wait.
|
||||
WaitUntilClean();
|
||||
}
|
||||
|
||||
Allocation allocation;
|
||||
allocation.host_ptr = host_base_ + write_head_;
|
||||
allocation.gpu_ptr = gpu_base_ + write_head_;
|
||||
allocation.offset = write_head_;
|
||||
allocation.length = length;
|
||||
allocation.aligned_length = aligned_length;
|
||||
allocation.cache_key = 0;
|
||||
write_head_ += aligned_length;
|
||||
return allocation;
|
||||
}
|
||||
|
||||
bool CircularBuffer::AcquireCached(uint32_t key, size_t length,
|
||||
Allocation* out_allocation) {
|
||||
uint64_t full_key = key | (length << 32);
|
||||
auto it = allocation_cache_.find(full_key);
|
||||
if (it != allocation_cache_.end()) {
|
||||
uintptr_t write_head = it->second;
|
||||
size_t aligned_length = xe::round_up(length, alignment_);
|
||||
out_allocation->host_ptr = host_base_ + write_head;
|
||||
out_allocation->gpu_ptr = gpu_base_ + write_head;
|
||||
out_allocation->offset = write_head;
|
||||
out_allocation->length = length;
|
||||
out_allocation->aligned_length = aligned_length;
|
||||
out_allocation->cache_key = full_key;
|
||||
return true;
|
||||
} else {
|
||||
*out_allocation = Acquire(length);
|
||||
out_allocation->cache_key = full_key;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CircularBuffer::Discard(Allocation allocation) {
|
||||
write_head_ -= allocation.aligned_length;
|
||||
}
|
||||
|
||||
void CircularBuffer::Commit(Allocation allocation) {
|
||||
uintptr_t start = allocation.gpu_ptr - gpu_base_;
|
||||
uintptr_t end = start + allocation.aligned_length;
|
||||
dirty_start_ = std::min(dirty_start_, start);
|
||||
dirty_end_ = std::max(dirty_end_, end);
|
||||
assert_true(dirty_end_ <= capacity_);
|
||||
if (allocation.cache_key) {
|
||||
allocation_cache_.insert({allocation.cache_key, allocation.offset});
|
||||
}
|
||||
}
|
||||
|
||||
void CircularBuffer::Flush() {
|
||||
if (dirty_start_ == dirty_end_ || dirty_start_ == UINT64_MAX) {
|
||||
return;
|
||||
}
|
||||
glFlushMappedNamedBufferRange(buffer_, dirty_start_,
|
||||
dirty_end_ - dirty_start_);
|
||||
dirty_start_ = UINT64_MAX;
|
||||
dirty_end_ = 0;
|
||||
}
|
||||
|
||||
void CircularBuffer::ClearCache() { allocation_cache_.clear(); }
|
||||
|
||||
void CircularBuffer::WaitUntilClean() {
|
||||
Flush();
|
||||
glFinish();
|
||||
write_head_ = 0;
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,71 +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_UI_GL_CIRCULAR_BUFFER_H_
|
||||
#define XENIA_UI_GL_CIRCULAR_BUFFER_H_
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/ui/gl/gl.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
// TODO(benvanik): uh, make this circular.
|
||||
// TODO(benvanik): fences to prevent this from ever flushing.
|
||||
class CircularBuffer {
|
||||
public:
|
||||
CircularBuffer(size_t capacity, size_t alignment = 256);
|
||||
~CircularBuffer();
|
||||
|
||||
struct Allocation {
|
||||
void* host_ptr;
|
||||
GLuint64 gpu_ptr;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
size_t aligned_length;
|
||||
uint64_t cache_key; // 0 if caching disabled.
|
||||
};
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
GLuint handle() const { return buffer_; }
|
||||
GLuint64 gpu_handle() const { return gpu_base_; }
|
||||
size_t capacity() const { return capacity_; }
|
||||
|
||||
bool CanAcquire(size_t length);
|
||||
Allocation Acquire(size_t length);
|
||||
bool AcquireCached(uint32_t key, size_t length, Allocation* out_allocation);
|
||||
void Discard(Allocation allocation);
|
||||
void Commit(Allocation allocation);
|
||||
void Flush();
|
||||
void ClearCache();
|
||||
|
||||
void WaitUntilClean();
|
||||
|
||||
private:
|
||||
size_t capacity_;
|
||||
size_t alignment_;
|
||||
uintptr_t write_head_;
|
||||
uintptr_t dirty_start_;
|
||||
uintptr_t dirty_end_;
|
||||
GLuint buffer_;
|
||||
GLuint64 gpu_base_;
|
||||
uint8_t* host_base_;
|
||||
|
||||
std::unordered_map<uint64_t, uintptr_t> allocation_cache_;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_CIRCULAR_BUFFER_H_
|
|
@ -1,32 +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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_GL_GL_H_
|
||||
#define XENIA_UI_GL_GL_H_
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#include "third_party/GL/glew.h"
|
||||
|
||||
typedef struct GLEWContextStruct GLEWContext;
|
||||
extern "C" GLEWContext* glewGetContext();
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
// We avoid including wglew.h here as it includes windows.h and pollutes the
|
||||
// global namespace. As we don't need wglew most places we only do that as
|
||||
// required.
|
||||
typedef struct WGLEWContextStruct WGLEWContext;
|
||||
extern "C" WGLEWContext* wglewGetContext();
|
||||
#elif XE_PLATFORM_LINUX
|
||||
typedef struct GLXEWContextStruct GLXEWContext;
|
||||
extern "C" GLXEWContext* glxewGetContext();
|
||||
|
||||
#endif
|
||||
|
||||
#endif // XENIA_UI_GL_GL_H_
|
|
@ -1,268 +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/ui/gl/gl_context.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/ui/gl/gl_immediate_drawer.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
DEFINE_bool(thread_safe_gl, false,
|
||||
"Only allow one GL context to be active at a time.");
|
||||
|
||||
DEFINE_bool(disable_gl_context_reset, false,
|
||||
"Do not aggressively reset the GL context (helps with capture "
|
||||
"programs such as OBS or FRAPS).");
|
||||
|
||||
DEFINE_bool(random_clear_color, false, "Randomizes GL clear color.");
|
||||
|
||||
DEFINE_bool(gl_debug, false, "Enable OpenGL debug validation layer.");
|
||||
DEFINE_bool(gl_debug_output, false, "Dump ARB_debug_output to stderr.");
|
||||
DEFINE_bool(gl_debug_output_synchronous, true,
|
||||
"ARB_debug_output will synchronize to be thread safe.");
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
std::recursive_mutex GLContext::global_gl_mutex_;
|
||||
|
||||
void GLContext::FatalGLError(std::string error) {
|
||||
xe::FatalError(
|
||||
error +
|
||||
"\nEnsure you have the latest drivers for your GPU and that it supports "
|
||||
"OpenGL 4.5. See http://xenia.jp/faq/ for more information and a list"
|
||||
"of supported GPUs.");
|
||||
}
|
||||
|
||||
GLContext::GLContext(GraphicsProvider* provider, Window* target_window)
|
||||
: GraphicsContext(provider, target_window) {}
|
||||
|
||||
GLContext::~GLContext() {}
|
||||
|
||||
void GLContext::AssertExtensionsPresent() {
|
||||
if (!MakeCurrent()) {
|
||||
FatalGLError("Unable to make GL context current.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check shader version at least 4.5 (matching GL 4.5).
|
||||
auto glsl_version_raw =
|
||||
reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
std::string glsl_version(glsl_version_raw);
|
||||
if (glsl_version.find("4.5") == std::string::npos &&
|
||||
glsl_version.find("4.6") == std::string::npos) {
|
||||
FatalGLError("OpenGL GLSL version 4.50 or higher is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GLEW_ARB_bindless_texture || !glMakeTextureHandleResidentARB) {
|
||||
FatalGLError("OpenGL extension ARB_bindless_texture is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GLEW_ARB_fragment_coord_conventions) {
|
||||
FatalGLError(
|
||||
"OpenGL extension ARB_fragment_coord_conventions is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
ClearCurrent();
|
||||
}
|
||||
|
||||
void GLContext::DebugMessage(GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length,
|
||||
const GLchar* message) {
|
||||
const char* source_name = nullptr;
|
||||
switch (source) {
|
||||
case GL_DEBUG_SOURCE_API_ARB:
|
||||
source_name = "OpenGL";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
|
||||
source_name = "Windows";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
|
||||
source_name = "Shader Compiler";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
|
||||
source_name = "Third Party";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION_ARB:
|
||||
source_name = "Application";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_OTHER_ARB:
|
||||
source_name = "Other";
|
||||
break;
|
||||
default:
|
||||
source_name = "(unknown source)";
|
||||
break;
|
||||
}
|
||||
|
||||
const char* type_name = nullptr;
|
||||
switch (type) {
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
type_name = "error";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
type_name = "deprecated behavior";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
type_name = "undefined behavior";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
type_name = "portability";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
type_name = "performance";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
type_name = "message";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_MARKER:
|
||||
type_name = "marker";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PUSH_GROUP:
|
||||
type_name = "push group";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_POP_GROUP:
|
||||
type_name = "pop group";
|
||||
break;
|
||||
default:
|
||||
type_name = "(unknown type)";
|
||||
break;
|
||||
}
|
||||
|
||||
const char* severity_name = nullptr;
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH_ARB:
|
||||
severity_name = "high";
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM_ARB:
|
||||
severity_name = "medium";
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_LOW_ARB:
|
||||
severity_name = "low";
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
severity_name = "notification";
|
||||
break;
|
||||
default:
|
||||
severity_name = "(unknown severity)";
|
||||
break;
|
||||
}
|
||||
|
||||
XELOGE("GL4 %s: %s(%s) %d: %s", source_name, type_name, severity_name, id,
|
||||
message);
|
||||
}
|
||||
|
||||
void GLAPIENTRY GLContext::DebugMessageThunk(GLenum source, GLenum type,
|
||||
GLuint id, GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
GLvoid* user_param) {
|
||||
reinterpret_cast<GLContext*>(user_param)
|
||||
->DebugMessage(source, type, id, severity, length, message);
|
||||
}
|
||||
|
||||
void GLContext::SetupDebugging() {
|
||||
if (!FLAGS_gl_debug || !FLAGS_gl_debug_output) {
|
||||
return;
|
||||
}
|
||||
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
|
||||
// Synchronous output hurts, but is required if we want to line up the logs.
|
||||
if (FLAGS_gl_debug_output_synchronous) {
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
} else {
|
||||
glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
}
|
||||
|
||||
// Enable everything by default.
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL,
|
||||
GL_TRUE);
|
||||
|
||||
// Disable annoying messages.
|
||||
GLuint disable_message_ids[] = {
|
||||
0x00020004, // Usage warning: Generic vertex attribute array 0 uses a
|
||||
// pointer with a small value (0x0000000000000000). Is this
|
||||
// intended to be used as an offset into a buffer object?
|
||||
};
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE,
|
||||
GLsizei(xe::countof(disable_message_ids)),
|
||||
disable_message_ids, GL_FALSE);
|
||||
|
||||
// Callback will be made from driver threads.
|
||||
glDebugMessageCallback(reinterpret_cast<GLDEBUGPROC>(&DebugMessageThunk),
|
||||
this);
|
||||
}
|
||||
|
||||
ImmediateDrawer* GLContext::immediate_drawer() {
|
||||
return immediate_drawer_.get();
|
||||
}
|
||||
|
||||
bool GLContext::WasLost() {
|
||||
if (!robust_access_supported_) {
|
||||
// Can't determine if we lost the context.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context_lost_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto status = glGetGraphicsResetStatusARB();
|
||||
if (status != GL_NO_ERROR) {
|
||||
// Graphics card reset.
|
||||
XELOGE("============= TDR detected on context %p! Context %s =============",
|
||||
handle(), status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent");
|
||||
context_lost_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<RawImage> GLContext::Capture() {
|
||||
GraphicsContextLock lock(this);
|
||||
|
||||
std::unique_ptr<RawImage> raw_image(new RawImage());
|
||||
raw_image->width = target_window_->width();
|
||||
raw_image->stride = raw_image->width * 4;
|
||||
raw_image->height = target_window_->height();
|
||||
raw_image->data.resize(raw_image->stride * raw_image->height);
|
||||
|
||||
glReadPixels(0, 0, target_window_->width(), target_window_->height(), GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, raw_image->data.data());
|
||||
|
||||
// Flip vertically in-place.
|
||||
size_t yt = 0;
|
||||
size_t yb = (raw_image->height - 1) * raw_image->stride;
|
||||
while (yt < yb) {
|
||||
for (size_t i = 0; i < raw_image->stride; ++i) {
|
||||
std::swap(raw_image->data[yt + i], raw_image->data[yb + i]);
|
||||
}
|
||||
yt += raw_image->stride;
|
||||
yb -= raw_image->stride;
|
||||
}
|
||||
|
||||
return raw_image;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,93 +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_UI_GL_GL_CONTEXT_H_
|
||||
#define XENIA_UI_GL_GL_CONTEXT_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "xenia/ui/gl/blitter.h"
|
||||
#include "xenia/ui/gl/gl.h"
|
||||
#include "xenia/ui/graphics_context.h"
|
||||
|
||||
DECLARE_bool(thread_safe_gl);
|
||||
|
||||
DECLARE_bool(disable_gl_context_reset);
|
||||
|
||||
DECLARE_bool(random_clear_color);
|
||||
|
||||
DECLARE_bool(gl_debug);
|
||||
DECLARE_bool(gl_debug_output);
|
||||
DECLARE_bool(gl_debug_output_synchronous);
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
class GLImmediateDrawer;
|
||||
class GLProvider;
|
||||
|
||||
class GLContext : public GraphicsContext {
|
||||
public:
|
||||
~GLContext() override;
|
||||
|
||||
ImmediateDrawer* immediate_drawer() override;
|
||||
|
||||
virtual bool is_current() override = 0;
|
||||
virtual bool MakeCurrent() override = 0;
|
||||
virtual void ClearCurrent() override = 0;
|
||||
bool WasLost() override;
|
||||
|
||||
virtual void BeginSwap() override = 0;
|
||||
virtual void EndSwap() override = 0;
|
||||
std::unique_ptr<RawImage> Capture() override;
|
||||
|
||||
Blitter* blitter() { return &blitter_; }
|
||||
|
||||
protected:
|
||||
Blitter blitter_;
|
||||
std::unique_ptr<GLImmediateDrawer> immediate_drawer_;
|
||||
|
||||
static std::recursive_mutex global_gl_mutex_;
|
||||
bool context_lost_ = false;
|
||||
bool robust_access_supported_ = false;
|
||||
static void FatalGLError(std::string error);
|
||||
virtual bool Initialize(GLContext* share_context) = 0;
|
||||
virtual void* handle() = 0;
|
||||
GLContext(GraphicsProvider* provider, Window* target_window);
|
||||
void SetupDebugging();
|
||||
void AssertExtensionsPresent();
|
||||
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar* message);
|
||||
|
||||
private:
|
||||
friend class GLProvider;
|
||||
|
||||
static std::unique_ptr<GLContext> Create(GraphicsProvider* provider,
|
||||
Window* target_window,
|
||||
GLContext* share_context = nullptr);
|
||||
static std::unique_ptr<GLContext> CreateOffscreen(GraphicsProvider* provider,
|
||||
GLContext* parent_context);
|
||||
|
||||
private:
|
||||
static void GLAPIENTRY DebugMessageThunk(GLenum source, GLenum type,
|
||||
GLuint id, GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
GLvoid* user_param);
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_GL_CONTEXT_H_
|
|
@ -1,315 +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/ui/gl/gl_context_win.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/ui/gl/gl_immediate_drawer.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
#include "third_party/GL/wglew.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
thread_local GLEWContext* tls_glew_context_ = nullptr;
|
||||
thread_local WGLEWContext* tls_wglew_context_ = nullptr;
|
||||
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
|
||||
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
|
||||
|
||||
std::unique_ptr<GLContext> GLContext::Create(GraphicsProvider* provider,
|
||||
Window* target_window,
|
||||
GLContext* share_context) {
|
||||
auto context =
|
||||
std::unique_ptr<GLContext>(new WGLContext(provider, target_window));
|
||||
if (!context->Initialize(share_context)) {
|
||||
return nullptr;
|
||||
}
|
||||
context->AssertExtensionsPresent();
|
||||
return context;
|
||||
}
|
||||
|
||||
std::unique_ptr<GLContext> GLContext::CreateOffscreen(
|
||||
GraphicsProvider* provider, GLContext* parent_context) {
|
||||
return WGLContext::CreateOffscreen(provider,
|
||||
static_cast<WGLContext*>(parent_context));
|
||||
}
|
||||
|
||||
WGLContext::WGLContext(GraphicsProvider* provider, Window* target_window)
|
||||
: GLContext(provider, target_window) {
|
||||
glew_context_.reset(new GLEWContext());
|
||||
wglew_context_.reset(new WGLEWContext());
|
||||
}
|
||||
|
||||
WGLContext::~WGLContext() {
|
||||
MakeCurrent();
|
||||
blitter_.Shutdown();
|
||||
immediate_drawer_.reset();
|
||||
ClearCurrent();
|
||||
if (glrc_) {
|
||||
wglDeleteContext(glrc_);
|
||||
}
|
||||
if (dc_) {
|
||||
ReleaseDC(HWND(target_window_->native_handle()), dc_);
|
||||
}
|
||||
}
|
||||
|
||||
bool WGLContext::Initialize(GLContext* share_context_) {
|
||||
WGLContext* share_context = static_cast<WGLContext*>(share_context_);
|
||||
dc_ = GetDC(HWND(target_window_->native_handle()));
|
||||
|
||||
PIXELFORMATDESCRIPTOR pfd = {0};
|
||||
pfd.nSize = sizeof(pfd);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.cColorBits = 32;
|
||||
pfd.cDepthBits = 32;
|
||||
pfd.iLayerType = PFD_MAIN_PLANE;
|
||||
int pixel_format = ChoosePixelFormat(dc_, &pfd);
|
||||
if (!pixel_format) {
|
||||
FatalGLError("Unable to choose pixel format.");
|
||||
return false;
|
||||
}
|
||||
if (!SetPixelFormat(dc_, pixel_format, &pfd)) {
|
||||
FatalGLError("Unable to set pixel format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
HGLRC temp_context = wglCreateContext(dc_);
|
||||
if (!temp_context) {
|
||||
FatalGLError("Unable to create temporary GL context.");
|
||||
return false;
|
||||
}
|
||||
wglMakeCurrent(dc_, temp_context);
|
||||
|
||||
tls_glew_context_ = glew_context_.get();
|
||||
tls_wglew_context_ = wglew_context_.get();
|
||||
if (glewInit() != GLEW_OK) {
|
||||
FatalGLError("Unable to initialize GLEW.");
|
||||
return false;
|
||||
}
|
||||
if (wglewInit() != GLEW_OK) {
|
||||
FatalGLError("Unable to initialize WGLEW.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WGLEW_ARB_create_context) {
|
||||
FatalGLError("WGL_ARG_create_context not supported by GL ICD.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GLEW_ARB_robustness) {
|
||||
robust_access_supported_ = true;
|
||||
}
|
||||
|
||||
int context_flags = 0;
|
||||
if (FLAGS_gl_debug) {
|
||||
context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
|
||||
}
|
||||
if (robust_access_supported_) {
|
||||
context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
|
||||
}
|
||||
|
||||
int attrib_list[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB,
|
||||
4,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB,
|
||||
5,
|
||||
WGL_CONTEXT_FLAGS_ARB,
|
||||
context_flags,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB,
|
||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0,
|
||||
0};
|
||||
|
||||
glrc_ = wglCreateContextAttribsARB(
|
||||
dc_, share_context ? share_context->glrc_ : nullptr, attrib_list);
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(temp_context);
|
||||
if (!glrc_) {
|
||||
FatalGLError("Unable to create real GL context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MakeCurrent()) {
|
||||
FatalGLError("Could not make real GL context current.");
|
||||
return false;
|
||||
}
|
||||
|
||||
XELOGI("Successfully created OpenGL context:");
|
||||
XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR));
|
||||
XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION));
|
||||
XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER));
|
||||
XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s",
|
||||
glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
|
||||
while (glGetError()) {
|
||||
// Clearing errors.
|
||||
}
|
||||
|
||||
SetupDebugging();
|
||||
|
||||
if (!blitter_.Initialize()) {
|
||||
FatalGLError("Unable to initialize blitter.");
|
||||
ClearCurrent();
|
||||
return false;
|
||||
}
|
||||
|
||||
immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
|
||||
|
||||
ClearCurrent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<WGLContext> WGLContext::CreateOffscreen(
|
||||
GraphicsProvider* provider, WGLContext* parent_context) {
|
||||
assert_not_null(parent_context->glrc_);
|
||||
|
||||
HGLRC new_glrc = nullptr;
|
||||
{
|
||||
GraphicsContextLock context_lock(parent_context);
|
||||
|
||||
int context_flags = 0;
|
||||
if (FLAGS_gl_debug) {
|
||||
context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
|
||||
}
|
||||
|
||||
bool robust_access_supported = parent_context->robust_access_supported_;
|
||||
if (robust_access_supported) {
|
||||
context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
|
||||
}
|
||||
|
||||
int attrib_list[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB,
|
||||
4,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB,
|
||||
5,
|
||||
WGL_CONTEXT_FLAGS_ARB,
|
||||
context_flags,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB,
|
||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
robust_access_supported ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0,
|
||||
0};
|
||||
new_glrc = wglCreateContextAttribsARB(parent_context->dc_,
|
||||
parent_context->glrc_, attrib_list);
|
||||
if (!new_glrc) {
|
||||
FatalGLError("Could not create shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto new_context = std::unique_ptr<WGLContext>(
|
||||
new WGLContext(provider, parent_context->target_window_));
|
||||
new_context->glrc_ = new_glrc;
|
||||
new_context->dc_ =
|
||||
GetDC(HWND(parent_context->target_window_->native_handle()));
|
||||
new_context->robust_access_supported_ =
|
||||
parent_context->robust_access_supported_;
|
||||
if (!new_context->MakeCurrent()) {
|
||||
FatalGLError("Could not make new GL context current.");
|
||||
return nullptr;
|
||||
}
|
||||
if (!glGetString(GL_EXTENSIONS)) {
|
||||
new_context->ClearCurrent();
|
||||
FatalGLError("New GL context did not have extensions.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (glewInit() != GLEW_OK) {
|
||||
new_context->ClearCurrent();
|
||||
FatalGLError("Unable to initialize GLEW on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
if (wglewInit() != GLEW_OK) {
|
||||
new_context->ClearCurrent();
|
||||
FatalGLError("Unable to initialize WGLEW on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
new_context->SetupDebugging();
|
||||
|
||||
if (!new_context->blitter_.Initialize()) {
|
||||
FatalGLError("Unable to initialize blitter on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
new_context->ClearCurrent();
|
||||
|
||||
return new_context;
|
||||
}
|
||||
|
||||
bool WGLContext::is_current() {
|
||||
return tls_glew_context_ == glew_context_.get();
|
||||
}
|
||||
|
||||
bool WGLContext::MakeCurrent() {
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.lock();
|
||||
}
|
||||
|
||||
if (!wglMakeCurrent(dc_, glrc_)) {
|
||||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.unlock();
|
||||
}
|
||||
FatalGLError("Unable to make GL context current.");
|
||||
return false;
|
||||
}
|
||||
tls_glew_context_ = glew_context_.get();
|
||||
tls_wglew_context_ = wglew_context_.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WGLContext::ClearCurrent() {
|
||||
if (!FLAGS_disable_gl_context_reset) {
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
}
|
||||
tls_glew_context_ = nullptr;
|
||||
tls_wglew_context_ = nullptr;
|
||||
|
||||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void WGLContext::BeginSwap() {
|
||||
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLContext::BeginSwap");
|
||||
float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f};
|
||||
if (FLAGS_random_clear_color) {
|
||||
clear_color[0] =
|
||||
rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
|
||||
clear_color[1] = 1.0f;
|
||||
clear_color[2] = 0.0f;
|
||||
clear_color[3] = 1.0f;
|
||||
}
|
||||
glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
|
||||
}
|
||||
|
||||
void WGLContext::EndSwap() {
|
||||
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLContext::EndSwap");
|
||||
SwapBuffers(dc_);
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,64 +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_UI_GL_WGL_CONTEXT_H_
|
||||
#define XENIA_UI_GL_WGL_CONTEXT_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/gl/blitter.h"
|
||||
#include "xenia/ui/gl/gl.h"
|
||||
#include "xenia/ui/gl/gl_context.h"
|
||||
#include "xenia/ui/graphics_context.h"
|
||||
|
||||
typedef struct HDC__* HDC;
|
||||
typedef struct HGLRC__* HGLRC;
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
class GLImmediateDrawer;
|
||||
class GLProvider;
|
||||
|
||||
class WGLContext : public GLContext {
|
||||
public:
|
||||
~WGLContext() override;
|
||||
|
||||
bool is_current() override;
|
||||
bool MakeCurrent() override;
|
||||
void ClearCurrent() override;
|
||||
|
||||
void BeginSwap() override;
|
||||
void EndSwap() override;
|
||||
|
||||
protected:
|
||||
friend class GLContext;
|
||||
WGLContext(GraphicsProvider* provider, Window* target_window);
|
||||
static std::unique_ptr<WGLContext> CreateOffscreen(
|
||||
GraphicsProvider* provider, WGLContext* parent_context);
|
||||
|
||||
bool Initialize(GLContext* share_context) override;
|
||||
void* handle() override { return glrc_; }
|
||||
|
||||
private:
|
||||
HDC dc_ = nullptr;
|
||||
HGLRC glrc_ = nullptr;
|
||||
|
||||
std::unique_ptr<GLEWContext> glew_context_;
|
||||
std::unique_ptr<WGLEWContext> wglew_context_;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_GL_CONTEXT_H_
|
|
@ -1,323 +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/ui/gl/gl_context_x11.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <gdk/gdkx.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "third_party/GL/glxew.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/platform_linux.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/ui/gl/gl_immediate_drawer.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
thread_local GLEWContext* tls_glew_context_ = nullptr;
|
||||
thread_local GLXEWContext* tls_glxew_context_ = nullptr;
|
||||
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
|
||||
extern "C" GLXEWContext* glxewGetContext() { return tls_glxew_context_; }
|
||||
|
||||
std::unique_ptr<GLContext> GLContext::Create(GraphicsProvider* provider,
|
||||
Window* target_window,
|
||||
GLContext* share_context) {
|
||||
auto context =
|
||||
std::unique_ptr<GLContext>(new GLXContext(provider, target_window));
|
||||
if (!context->Initialize(share_context)) {
|
||||
return nullptr;
|
||||
}
|
||||
context->AssertExtensionsPresent();
|
||||
return context;
|
||||
}
|
||||
|
||||
std::unique_ptr<GLContext> GLContext::CreateOffscreen(
|
||||
GraphicsProvider* provider, GLContext* parent_context) {
|
||||
return GLXContext::CreateOffscreen(provider,
|
||||
static_cast<GLXContext*>(parent_context));
|
||||
}
|
||||
|
||||
GLXContext::GLXContext(GraphicsProvider* provider, Window* target_window)
|
||||
: GLContext(provider, target_window) {
|
||||
glew_context_.reset(new GLEWContext());
|
||||
glxew_context_.reset(new GLXEWContext());
|
||||
}
|
||||
|
||||
GLXContext::~GLXContext() {
|
||||
MakeCurrent();
|
||||
blitter_.Shutdown();
|
||||
immediate_drawer_.reset();
|
||||
ClearCurrent();
|
||||
if (glx_context_) {
|
||||
glXDestroyContext(disp_, glx_context_);
|
||||
}
|
||||
if (draw_area_) {
|
||||
gtk_widget_destroy(draw_area_);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLXContext::Initialize(GLContext* share_context) {
|
||||
GtkWidget* window = GTK_WIDGET(target_window_->native_handle());
|
||||
GtkWidget* draw_area = gtk_drawing_area_new();
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
gtk_window_get_size(GTK_WINDOW(window), &width, &height);
|
||||
gtk_widget_set_size_request(draw_area, width, height);
|
||||
gtk_container_add(GTK_CONTAINER(window), draw_area);
|
||||
GdkVisual* visual = gdk_screen_get_system_visual(gdk_screen_get_default());
|
||||
|
||||
GdkDisplay* gdk_display = gtk_widget_get_display(window);
|
||||
Display* display = gdk_x11_display_get_xdisplay(gdk_display);
|
||||
disp_ = display;
|
||||
::Window root = gdk_x11_get_default_root_xwindow();
|
||||
static int vis_attrib_list[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24,
|
||||
GLX_DOUBLEBUFFER, None};
|
||||
XVisualInfo* vi = glXChooseVisual(display, 0, vis_attrib_list);
|
||||
if (vi == NULL) {
|
||||
FatalGLError("No matching visuals for X display");
|
||||
return false;
|
||||
}
|
||||
|
||||
cmap_ = XCreateColormap(display, root, vi->visual, AllocNone);
|
||||
|
||||
::GLXContext temp_context = glXCreateContext(display, vi, NULL, GL_TRUE);
|
||||
if (!temp_context) {
|
||||
FatalGLError("Unable to create temporary GLX context");
|
||||
return false;
|
||||
}
|
||||
xid_ = GDK_WINDOW_XID(gtk_widget_get_window(window));
|
||||
glXMakeCurrent(display, xid_, temp_context);
|
||||
|
||||
tls_glew_context_ = glew_context_.get();
|
||||
tls_glxew_context_ = glxew_context_.get();
|
||||
if (glewInit() != GLEW_OK) {
|
||||
FatalGLError("Unable to initialize GLEW.");
|
||||
return false;
|
||||
}
|
||||
if (glxewInit() != GLEW_OK) {
|
||||
FatalGLError("Unable to initialize GLXEW.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GLXEW_ARB_create_context) {
|
||||
FatalGLError("GLX_ARB_create_context not supported by GL ICD.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GLEW_ARB_robustness) {
|
||||
robust_access_supported_ = true;
|
||||
}
|
||||
|
||||
int context_flags = 0;
|
||||
if (FLAGS_gl_debug) {
|
||||
context_flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
|
||||
}
|
||||
if (robust_access_supported_) {
|
||||
context_flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
|
||||
}
|
||||
|
||||
int attrib_list[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB,
|
||||
4,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
5,
|
||||
GLX_CONTEXT_FLAGS_ARB,
|
||||
context_flags,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB,
|
||||
GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
robust_access_supported_ ? GLX_LOSE_CONTEXT_ON_RESET_ARB : 0,
|
||||
0};
|
||||
GLXContext* share_context_glx = static_cast<GLXContext*>(share_context);
|
||||
glx_context_ = glXCreateContextAttribsARB(
|
||||
display, nullptr,
|
||||
share_context ? share_context_glx->glx_context_ : nullptr, True,
|
||||
attrib_list);
|
||||
glXMakeCurrent(display, 0, nullptr);
|
||||
glXDestroyContext(display, temp_context);
|
||||
if (!glx_context_) {
|
||||
FatalGLError("Unable to create real GL context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MakeCurrent()) {
|
||||
FatalGLError("Could not make real GL context current.");
|
||||
return false;
|
||||
}
|
||||
|
||||
XELOGI("Successfully created OpenGL context:");
|
||||
XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR));
|
||||
XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION));
|
||||
XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER));
|
||||
XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s",
|
||||
glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
|
||||
while (glGetError()) {
|
||||
// Clearing errors.
|
||||
}
|
||||
|
||||
SetupDebugging();
|
||||
|
||||
if (!blitter_.Initialize()) {
|
||||
FatalGLError("Unable to initialize blitter.");
|
||||
ClearCurrent();
|
||||
return false;
|
||||
}
|
||||
|
||||
immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
|
||||
|
||||
ClearCurrent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<GLXContext> GLXContext::CreateOffscreen(
|
||||
GraphicsProvider* provider, GLXContext* parent_context) {
|
||||
assert_not_null(parent_context->glx_context_);
|
||||
|
||||
::GLXContext new_glrc;
|
||||
{
|
||||
GraphicsContextLock context_lock(parent_context);
|
||||
|
||||
int context_flags = 0;
|
||||
if (FLAGS_gl_debug) {
|
||||
context_flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
|
||||
}
|
||||
|
||||
bool robust_access_supported = parent_context->robust_access_supported_;
|
||||
if (robust_access_supported) {
|
||||
context_flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
|
||||
}
|
||||
|
||||
int attrib_list[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB,
|
||||
4,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB,
|
||||
5,
|
||||
GLX_CONTEXT_FLAGS_ARB,
|
||||
context_flags,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB,
|
||||
GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
robust_access_supported ? GLX_LOSE_CONTEXT_ON_RESET_ARB : 0,
|
||||
0};
|
||||
new_glrc = glXCreateContextAttribsARB(parent_context->disp_, nullptr,
|
||||
parent_context->glx_context_, True,
|
||||
attrib_list);
|
||||
if (!new_glrc) {
|
||||
FatalGLError("Could not create shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto new_context = std::unique_ptr<GLXContext>(
|
||||
new GLXContext(provider, parent_context->target_window_));
|
||||
new_context->glx_context_ = new_glrc;
|
||||
new_context->window_ = parent_context->window_;
|
||||
new_context->draw_area_ = parent_context->draw_area_;
|
||||
new_context->disp_ = parent_context->disp_;
|
||||
new_context->xid_ = parent_context->xid_;
|
||||
new_context->robust_access_supported_ =
|
||||
parent_context->robust_access_supported_;
|
||||
if (!new_context->MakeCurrent()) {
|
||||
FatalGLError("Could not make new GL context current.");
|
||||
return nullptr;
|
||||
}
|
||||
if (!glGetString(GL_EXTENSIONS)) {
|
||||
new_context->ClearCurrent();
|
||||
FatalGLError("New GL context did not have extensions.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (glewInit() != GLEW_OK) {
|
||||
new_context->ClearCurrent();
|
||||
FatalGLError("Unable to initialize GLEW on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
if (glxewInit() != GLEW_OK) {
|
||||
new_context->ClearCurrent();
|
||||
FatalGLError("Unable to initialize GLXEW on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
new_context->SetupDebugging();
|
||||
|
||||
if (!new_context->blitter_.Initialize()) {
|
||||
FatalGLError("Unable to initialize blitter on shared context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
new_context->ClearCurrent();
|
||||
|
||||
return new_context;
|
||||
}
|
||||
|
||||
bool GLXContext::is_current() {
|
||||
return tls_glew_context_ == glew_context_.get();
|
||||
}
|
||||
|
||||
bool GLXContext::MakeCurrent() {
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.lock();
|
||||
}
|
||||
|
||||
if (!glXMakeCurrent(disp_, xid_, glx_context_)) {
|
||||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.unlock();
|
||||
}
|
||||
FatalGLError("Unable to make GL context current.");
|
||||
return false;
|
||||
}
|
||||
tls_glew_context_ = glew_context_.get();
|
||||
tls_glxew_context_ = glxew_context_.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLXContext::ClearCurrent() {
|
||||
if (!FLAGS_disable_gl_context_reset) {
|
||||
glXMakeCurrent(disp_, 0, nullptr);
|
||||
}
|
||||
tls_glew_context_ = nullptr;
|
||||
tls_glxew_context_ = nullptr;
|
||||
|
||||
if (FLAGS_thread_safe_gl) {
|
||||
global_gl_mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void GLXContext::BeginSwap() {
|
||||
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLXContext::BeginSwap");
|
||||
float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f};
|
||||
if (FLAGS_random_clear_color) {
|
||||
clear_color[0] =
|
||||
rand() / static_cast<float>(RAND_MAX); // NOLINT(runtime/threadsafe_fn)
|
||||
clear_color[1] = 1.0f;
|
||||
clear_color[2] = 0.0f;
|
||||
clear_color[3] = 1.0f;
|
||||
}
|
||||
glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color);
|
||||
}
|
||||
|
||||
void GLXContext::EndSwap() {
|
||||
SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLXContext::EndSwap");
|
||||
glXSwapBuffers(disp_, xid_);
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,69 +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_UI_GL_GLX_CONTEXT_H_
|
||||
#define XENIA_UI_GL_GLX_CONTEXT_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "third_party/GL/glxew.h"
|
||||
#include "xenia/base/platform_linux.h"
|
||||
#include "xenia/ui/gl/blitter.h"
|
||||
#include "xenia/ui/gl/gl.h"
|
||||
#include "xenia/ui/gl/gl_context.h"
|
||||
#include "xenia/ui/graphics_context.h"
|
||||
|
||||
DECLARE_bool(thread_safe_gl);
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
class GLImmediateDrawer;
|
||||
class GLProvider;
|
||||
|
||||
class GLXContext : public GLContext {
|
||||
public:
|
||||
~GLXContext() override;
|
||||
|
||||
bool is_current() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
void ClearCurrent() override;
|
||||
|
||||
void BeginSwap() override;
|
||||
void EndSwap() override;
|
||||
|
||||
protected:
|
||||
static std::unique_ptr<GLXContext> CreateOffscreen(
|
||||
GraphicsProvider* provider, GLXContext* parent_context);
|
||||
|
||||
bool Initialize(GLContext* share_context) override;
|
||||
void* handle() override { return glx_context_; }
|
||||
|
||||
private:
|
||||
friend class GLContext;
|
||||
GLXContext(GraphicsProvider* provider, Window* target_window);
|
||||
std::unique_ptr<GLEWContext> glew_context_;
|
||||
std::unique_ptr<GLXEWContext> glxew_context_;
|
||||
::GLXContext glx_context_;
|
||||
GtkWidget* window_;
|
||||
GtkWidget* draw_area_;
|
||||
Colormap cmap_;
|
||||
Display* disp_;
|
||||
int xid_;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_GL_CONTEXT_H_
|
|
@ -1,283 +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/ui/gl/gl_immediate_drawer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/ui/graphics_context.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
constexpr uint32_t kMaxDrawVertices = 64 * 1024;
|
||||
constexpr uint32_t kMaxDrawIndices = 64 * 1024;
|
||||
|
||||
class GLImmediateTexture : public ImmediateTexture {
|
||||
public:
|
||||
GLImmediateTexture(uint32_t width, uint32_t height,
|
||||
ImmediateTextureFilter filter, bool repeat)
|
||||
: ImmediateTexture(width, height) {
|
||||
GLuint gl_handle;
|
||||
glCreateTextures(GL_TEXTURE_2D, 1, &gl_handle);
|
||||
|
||||
GLenum gl_filter = GL_NEAREST;
|
||||
switch (filter) {
|
||||
case ImmediateTextureFilter::kNearest:
|
||||
gl_filter = GL_NEAREST;
|
||||
break;
|
||||
case ImmediateTextureFilter::kLinear:
|
||||
gl_filter = GL_LINEAR;
|
||||
break;
|
||||
}
|
||||
glTextureParameteri(gl_handle, GL_TEXTURE_MIN_FILTER, gl_filter);
|
||||
glTextureParameteri(gl_handle, GL_TEXTURE_MAG_FILTER, gl_filter);
|
||||
|
||||
glTextureParameteri(gl_handle, GL_TEXTURE_WRAP_S,
|
||||
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(gl_handle, GL_TEXTURE_WRAP_T,
|
||||
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||
|
||||
glTextureStorage2D(gl_handle, 1, GL_RGBA8, width, height);
|
||||
|
||||
handle = static_cast<uintptr_t>(gl_handle);
|
||||
}
|
||||
|
||||
~GLImmediateTexture() override {
|
||||
GLuint gl_handle = static_cast<GLuint>(handle);
|
||||
glDeleteTextures(1, &gl_handle);
|
||||
}
|
||||
};
|
||||
|
||||
GLImmediateDrawer::GLImmediateDrawer(GraphicsContext* graphics_context)
|
||||
: ImmediateDrawer(graphics_context) {
|
||||
glCreateBuffers(1, &vertex_buffer_);
|
||||
glNamedBufferStorage(vertex_buffer_,
|
||||
kMaxDrawVertices * sizeof(ImmediateVertex), nullptr,
|
||||
GL_DYNAMIC_STORAGE_BIT);
|
||||
glCreateBuffers(1, &index_buffer_);
|
||||
glNamedBufferStorage(index_buffer_, kMaxDrawIndices * sizeof(uint16_t),
|
||||
nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||
|
||||
glCreateVertexArrays(1, &vao_);
|
||||
glEnableVertexArrayAttrib(vao_, 0);
|
||||
glVertexArrayAttribBinding(vao_, 0, 0);
|
||||
glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ImmediateVertex, x));
|
||||
glEnableVertexArrayAttrib(vao_, 1);
|
||||
glVertexArrayAttribBinding(vao_, 1, 0);
|
||||
glVertexArrayAttribFormat(vao_, 1, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ImmediateVertex, u));
|
||||
glEnableVertexArrayAttrib(vao_, 2);
|
||||
glVertexArrayAttribBinding(vao_, 2, 0);
|
||||
glVertexArrayAttribFormat(vao_, 2, 4, GL_UNSIGNED_BYTE, GL_TRUE,
|
||||
offsetof(ImmediateVertex, color));
|
||||
glVertexArrayVertexBuffer(vao_, 0, vertex_buffer_, 0,
|
||||
sizeof(ImmediateVertex));
|
||||
|
||||
InitializeShaders();
|
||||
}
|
||||
|
||||
GLImmediateDrawer::~GLImmediateDrawer() {
|
||||
GraphicsContextLock lock(graphics_context_);
|
||||
glDeleteBuffers(1, &vertex_buffer_);
|
||||
glDeleteBuffers(1, &index_buffer_);
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
glDeleteProgram(program_);
|
||||
}
|
||||
|
||||
void GLImmediateDrawer::InitializeShaders() {
|
||||
const std::string header =
|
||||
R"(
|
||||
#version 450
|
||||
#extension GL_ARB_explicit_uniform_location : require
|
||||
#extension GL_ARB_shading_language_420pack : require
|
||||
precision highp float;
|
||||
layout(std140, column_major) uniform;
|
||||
layout(std430, column_major) buffer;
|
||||
)";
|
||||
const std::string vertex_shader_source = header +
|
||||
R"(
|
||||
layout(location = 0) uniform mat4 projection_matrix;
|
||||
layout(location = 0) in vec2 in_pos;
|
||||
layout(location = 1) in vec2 in_uv;
|
||||
layout(location = 2) in vec4 in_color;
|
||||
layout(location = 0) out vec2 vtx_uv;
|
||||
layout(location = 1) out vec4 vtx_color;
|
||||
void main() {
|
||||
gl_Position = projection_matrix * vec4(in_pos.xy, 0.0, 1.0);
|
||||
vtx_uv = in_uv;
|
||||
vtx_color = in_color;
|
||||
})";
|
||||
const std::string fragment_shader_source = header +
|
||||
R"(
|
||||
layout(location = 1) uniform sampler2D texture_sampler;
|
||||
layout(location = 2) uniform int restrict_texture_samples;
|
||||
layout(location = 0) in vec2 vtx_uv;
|
||||
layout(location = 1) in vec4 vtx_color;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
void main() {
|
||||
out_color = vtx_color;
|
||||
if (restrict_texture_samples == 0 || vtx_uv.x <= 1.0) {
|
||||
vec4 tex_color = texture(texture_sampler, vtx_uv);
|
||||
out_color *= tex_color;
|
||||
// TODO(benvanik): microprofiler shadows.
|
||||
}
|
||||
})";
|
||||
|
||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||
const char* vertex_shader_source_ptr = vertex_shader_source.c_str();
|
||||
GLint vertex_shader_source_length = GLint(vertex_shader_source.size());
|
||||
glShaderSource(vertex_shader, 1, &vertex_shader_source_ptr,
|
||||
&vertex_shader_source_length);
|
||||
glCompileShader(vertex_shader);
|
||||
|
||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
const char* fragment_shader_source_ptr = fragment_shader_source.c_str();
|
||||
GLint fragment_shader_source_length = GLint(fragment_shader_source.size());
|
||||
glShaderSource(fragment_shader, 1, &fragment_shader_source_ptr,
|
||||
&fragment_shader_source_length);
|
||||
glCompileShader(fragment_shader);
|
||||
|
||||
program_ = glCreateProgram();
|
||||
glAttachShader(program_, vertex_shader);
|
||||
glAttachShader(program_, fragment_shader);
|
||||
glLinkProgram(program_);
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
}
|
||||
|
||||
std::unique_ptr<ImmediateTexture> GLImmediateDrawer::CreateTexture(
|
||||
uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat,
|
||||
const uint8_t* data) {
|
||||
GraphicsContextLock lock(graphics_context_);
|
||||
auto texture =
|
||||
std::make_unique<GLImmediateTexture>(width, height, filter, repeat);
|
||||
if (data) {
|
||||
UpdateTexture(texture.get(), data);
|
||||
}
|
||||
return std::unique_ptr<ImmediateTexture>(texture.release());
|
||||
}
|
||||
|
||||
void GLImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
|
||||
const uint8_t* data) {
|
||||
GraphicsContextLock lock(graphics_context_);
|
||||
glTextureSubImage2D(static_cast<GLuint>(texture->handle), 0, 0, 0,
|
||||
texture->width, texture->height, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
void GLImmediateDrawer::Begin(int render_target_width,
|
||||
int render_target_height) {
|
||||
was_current_ = graphics_context_->is_current();
|
||||
if (!was_current_) {
|
||||
graphics_context_->MakeCurrent();
|
||||
}
|
||||
|
||||
// Setup render state.
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glBlendEquationi(0, GL_FUNC_ADD);
|
||||
glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// Prepare drawing resources.
|
||||
glUseProgram(program_);
|
||||
glBindVertexArray(vao_);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
|
||||
|
||||
// Setup orthographic projection matrix and viewport.
|
||||
const float ortho_projection[4][4] = {
|
||||
{2.0f / render_target_width, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 2.0f / -render_target_height, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, -1.0f, 0.0f},
|
||||
{-1.0f, 1.0f, 0.0f, 1.0f},
|
||||
};
|
||||
glProgramUniformMatrix4fv(program_, 0, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
glViewport(0, 0, render_target_width, render_target_height);
|
||||
}
|
||||
|
||||
void GLImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
||||
assert_true(batch.vertex_count <= kMaxDrawVertices);
|
||||
glNamedBufferSubData(vertex_buffer_, 0,
|
||||
batch.vertex_count * sizeof(ImmediateVertex),
|
||||
batch.vertices);
|
||||
if (batch.indices) {
|
||||
assert_true(batch.index_count <= kMaxDrawIndices);
|
||||
glNamedBufferSubData(index_buffer_, 0, batch.index_count * sizeof(uint16_t),
|
||||
batch.indices);
|
||||
}
|
||||
|
||||
batch_has_index_buffer_ = !!batch.indices;
|
||||
}
|
||||
|
||||
void GLImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||
if (draw.scissor) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissorIndexed(0, draw.scissor_rect[0], draw.scissor_rect[1],
|
||||
draw.scissor_rect[2], draw.scissor_rect[3]);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
if (draw.alpha_blend) {
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glBlendEquationi(0, GL_FUNC_ADD);
|
||||
glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
glDisablei(GL_BLEND, 0);
|
||||
}
|
||||
|
||||
if (draw.texture_handle) {
|
||||
glBindTextureUnit(0, static_cast<GLuint>(draw.texture_handle));
|
||||
} else {
|
||||
glBindTextureUnit(0, 0);
|
||||
}
|
||||
glProgramUniform1i(program_, 2, draw.restrict_texture_samples ? 1 : 0);
|
||||
|
||||
GLenum mode = GL_TRIANGLES;
|
||||
switch (draw.primitive_type) {
|
||||
case ImmediatePrimitiveType::kLines:
|
||||
mode = GL_LINES;
|
||||
break;
|
||||
case ImmediatePrimitiveType::kTriangles:
|
||||
mode = GL_TRIANGLES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (batch_has_index_buffer_) {
|
||||
glDrawElementsBaseVertex(
|
||||
mode, draw.count, GL_UNSIGNED_SHORT,
|
||||
reinterpret_cast<void*>(draw.index_offset * sizeof(uint16_t)),
|
||||
draw.base_vertex);
|
||||
} else {
|
||||
glDrawArrays(mode, draw.base_vertex, draw.count);
|
||||
}
|
||||
}
|
||||
|
||||
void GLImmediateDrawer::EndDrawBatch() { glFlush(); }
|
||||
|
||||
void GLImmediateDrawer::End() {
|
||||
// Restore modified state.
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindTextureUnit(0, 0);
|
||||
glUseProgram(0);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
if (!was_current_) {
|
||||
graphics_context_->ClearCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,56 +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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_
|
||||
#define XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/gl/gl.h"
|
||||
#include "xenia/ui/immediate_drawer.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
class GLImmediateDrawer : public ImmediateDrawer {
|
||||
public:
|
||||
GLImmediateDrawer(GraphicsContext* graphics_context);
|
||||
~GLImmediateDrawer() override;
|
||||
|
||||
std::unique_ptr<ImmediateTexture> CreateTexture(uint32_t width,
|
||||
uint32_t height,
|
||||
ImmediateTextureFilter filter,
|
||||
bool repeat,
|
||||
const uint8_t* data) override;
|
||||
void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override;
|
||||
|
||||
void Begin(int render_target_width, int render_target_height) override;
|
||||
void BeginDrawBatch(const ImmediateDrawBatch& batch) override;
|
||||
void Draw(const ImmediateDraw& draw) override;
|
||||
void EndDrawBatch() override;
|
||||
void End() override;
|
||||
|
||||
private:
|
||||
void InitializeShaders();
|
||||
|
||||
GLuint program_ = 0;
|
||||
GLuint vao_ = 0;
|
||||
GLuint vertex_buffer_ = 0;
|
||||
GLuint index_buffer_ = 0;
|
||||
|
||||
bool was_current_ = false;
|
||||
bool batch_has_index_buffer_ = false;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_GL_IMMEDIATE_DRAWER_H_
|
|
@ -1,49 +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/ui/gl/gl_provider.h"
|
||||
|
||||
#include "xenia/ui/gl/gl_context.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
std::unique_ptr<GraphicsProvider> GLProvider::Create(Window* main_window) {
|
||||
std::unique_ptr<GLProvider> provider(new GLProvider(main_window));
|
||||
|
||||
//
|
||||
|
||||
return std::unique_ptr<GraphicsProvider>(provider.release());
|
||||
}
|
||||
|
||||
GLProvider::GLProvider(Window* main_window) : GraphicsProvider(main_window) {}
|
||||
|
||||
GLProvider::~GLProvider() = default;
|
||||
|
||||
std::unique_ptr<GraphicsContext> GLProvider::CreateContext(
|
||||
Window* target_window) {
|
||||
auto share_context = main_window_->context();
|
||||
return std::unique_ptr<GraphicsContext>(
|
||||
GLContext::Create(this, target_window,
|
||||
static_cast<GLContext*>(share_context))
|
||||
.release());
|
||||
}
|
||||
|
||||
std::unique_ptr<GraphicsContext> GLProvider::CreateOffscreenContext() {
|
||||
auto share_context = main_window_->context();
|
||||
return std::unique_ptr<GraphicsContext>(
|
||||
GLContext::CreateOffscreen(this, static_cast<GLContext*>(share_context))
|
||||
.release());
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
|
@ -1,40 +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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_GL_GL_PROVIDER_H_
|
||||
#define XENIA_UI_GL_GL_PROVIDER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/graphics_provider.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
class GLProvider : public GraphicsProvider {
|
||||
public:
|
||||
~GLProvider() override;
|
||||
|
||||
static std::unique_ptr<GraphicsProvider> Create(Window* main_window);
|
||||
|
||||
std::unique_ptr<GraphicsContext> CreateContext(
|
||||
Window* target_window) override;
|
||||
|
||||
std::unique_ptr<GraphicsContext> CreateOffscreenContext() override;
|
||||
|
||||
protected:
|
||||
explicit GLProvider(Window* main_window);
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_GL_GL_PROVIDER_H_
|
|
@ -1,30 +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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/main.h"
|
||||
#include "xenia/ui/gl/gl_provider.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
int window_demo_main(const std::vector<std::wstring>& args);
|
||||
|
||||
std::unique_ptr<GraphicsProvider> CreateDemoGraphicsProvider(Window* window) {
|
||||
return xe::ui::gl::GLProvider::Create(window);
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-ui-window-gl-demo", L"xenia-ui-window-gl-demo",
|
||||
xe::ui::window_demo_main);
|
|
@ -1,65 +0,0 @@
|
|||
project_root = "../../../.."
|
||||
include(project_root.."/tools/build")
|
||||
|
||||
group("src")
|
||||
project("xenia-ui-gl")
|
||||
uuid("623300e3-0085-4ccc-af46-d60e88cb43aa")
|
||||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"glew",
|
||||
"xenia-base",
|
||||
"xenia-ui",
|
||||
})
|
||||
defines({
|
||||
"GLEW_STATIC=1",
|
||||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/gflags/src",
|
||||
})
|
||||
local_platform_files()
|
||||
removefiles({"*_demo.cc"})
|
||||
|
||||
group("demos")
|
||||
project("xenia-ui-window-gl-demo")
|
||||
uuid("e0a687e5-d1f4-4c18-b2f7-012c53ec1ee4")
|
||||
kind("WindowedApp")
|
||||
language("C++")
|
||||
links({
|
||||
"gflags",
|
||||
"glew",
|
||||
"imgui",
|
||||
"xenia-base",
|
||||
"xenia-ui",
|
||||
"xenia-ui-gl",
|
||||
})
|
||||
filter("platforms:Linux")
|
||||
links({
|
||||
"X11",
|
||||
"xcb",
|
||||
"X11-xcb",
|
||||
"GL",
|
||||
"vulkan",
|
||||
})
|
||||
filter()
|
||||
flags({
|
||||
"WinMain", -- Use WinMain instead of main.
|
||||
})
|
||||
defines({
|
||||
"GLEW_STATIC=1",
|
||||
"GLEW_MX=1",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/gflags/src",
|
||||
})
|
||||
files({
|
||||
"../window_demo.cc",
|
||||
"gl_window_demo.cc",
|
||||
project_root.."/src/xenia/base/main_"..platform_suffix..".cc",
|
||||
})
|
||||
files({
|
||||
})
|
||||
resincludedirs({
|
||||
project_root,
|
||||
})
|
Loading…
Reference in New Issue