From 040d8d1ade74f4c3f55e6ae6b5b3bfe692dc8128 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Fri, 4 May 2018 11:54:33 -0500 Subject: [PATCH] [GL4] Farewell, GL4 backend --- premake5.lua | 2 - src/xenia/app/premake5.lua | 2 - src/xenia/app/xenia_main.cc | 8 +- src/xenia/gpu/gl4/draw_batcher.cc | 535 ----- src/xenia/gpu/gl4/draw_batcher.h | 191 -- src/xenia/gpu/gl4/gl4_command_processor.cc | 2131 -------------------- src/xenia/gpu/gl4/gl4_command_processor.h | 237 --- src/xenia/gpu/gl4/gl4_gpu_flags.cc | 17 - src/xenia/gpu/gl4/gl4_gpu_flags.h | 21 - src/xenia/gpu/gl4/gl4_graphics_system.cc | 86 - src/xenia/gpu/gl4/gl4_graphics_system.h | 45 - src/xenia/gpu/gl4/gl4_shader.cc | 298 --- src/xenia/gpu/gl4/gl4_shader.h | 54 - src/xenia/gpu/gl4/gl4_shader_cache.cc | 187 -- src/xenia/gpu/gl4/gl4_shader_cache.h | 62 - src/xenia/gpu/gl4/gl4_trace_viewer_main.cc | 109 - src/xenia/gpu/gl4/premake5.lua | 97 - src/xenia/gpu/gl4/texture_cache.cc | 1101 ---------- src/xenia/gpu/gl4/texture_cache.h | 119 -- src/xenia/hid/premake5.lua | 1 - src/xenia/ui/gl/blitter.cc | 315 --- src/xenia/ui/gl/blitter.h | 70 - src/xenia/ui/gl/circular_buffer.cc | 139 -- src/xenia/ui/gl/circular_buffer.h | 71 - src/xenia/ui/gl/gl.h | 32 - src/xenia/ui/gl/gl_context.cc | 268 --- src/xenia/ui/gl/gl_context.h | 93 - src/xenia/ui/gl/gl_context_win.cc | 315 --- src/xenia/ui/gl/gl_context_win.h | 64 - src/xenia/ui/gl/gl_context_x11.cc | 323 --- src/xenia/ui/gl/gl_context_x11.h | 69 - src/xenia/ui/gl/gl_immediate_drawer.cc | 283 --- src/xenia/ui/gl/gl_immediate_drawer.h | 56 - src/xenia/ui/gl/gl_provider.cc | 49 - src/xenia/ui/gl/gl_provider.h | 40 - src/xenia/ui/gl/gl_window_demo.cc | 30 - src/xenia/ui/gl/premake5.lua | 65 - 37 files changed, 2 insertions(+), 7583 deletions(-) delete mode 100644 src/xenia/gpu/gl4/draw_batcher.cc delete mode 100644 src/xenia/gpu/gl4/draw_batcher.h delete mode 100644 src/xenia/gpu/gl4/gl4_command_processor.cc delete mode 100644 src/xenia/gpu/gl4/gl4_command_processor.h delete mode 100644 src/xenia/gpu/gl4/gl4_gpu_flags.cc delete mode 100644 src/xenia/gpu/gl4/gl4_gpu_flags.h delete mode 100644 src/xenia/gpu/gl4/gl4_graphics_system.cc delete mode 100644 src/xenia/gpu/gl4/gl4_graphics_system.h delete mode 100644 src/xenia/gpu/gl4/gl4_shader.cc delete mode 100644 src/xenia/gpu/gl4/gl4_shader.h delete mode 100644 src/xenia/gpu/gl4/gl4_shader_cache.cc delete mode 100644 src/xenia/gpu/gl4/gl4_shader_cache.h delete mode 100644 src/xenia/gpu/gl4/gl4_trace_viewer_main.cc delete mode 100644 src/xenia/gpu/gl4/premake5.lua delete mode 100644 src/xenia/gpu/gl4/texture_cache.cc delete mode 100644 src/xenia/gpu/gl4/texture_cache.h delete mode 100644 src/xenia/ui/gl/blitter.cc delete mode 100644 src/xenia/ui/gl/blitter.h delete mode 100644 src/xenia/ui/gl/circular_buffer.cc delete mode 100644 src/xenia/ui/gl/circular_buffer.h delete mode 100644 src/xenia/ui/gl/gl.h delete mode 100644 src/xenia/ui/gl/gl_context.cc delete mode 100644 src/xenia/ui/gl/gl_context.h delete mode 100644 src/xenia/ui/gl/gl_context_win.cc delete mode 100644 src/xenia/ui/gl/gl_context_win.h delete mode 100644 src/xenia/ui/gl/gl_context_x11.cc delete mode 100644 src/xenia/ui/gl/gl_context_x11.h delete mode 100644 src/xenia/ui/gl/gl_immediate_drawer.cc delete mode 100644 src/xenia/ui/gl/gl_immediate_drawer.h delete mode 100644 src/xenia/ui/gl/gl_provider.cc delete mode 100644 src/xenia/ui/gl/gl_provider.h delete mode 100644 src/xenia/ui/gl/gl_window_demo.cc delete mode 100644 src/xenia/ui/gl/premake5.lua diff --git a/premake5.lua b/premake5.lua index 5412aad33..a3dd2a3c8 100644 --- a/premake5.lua +++ b/premake5.lua @@ -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") diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 4f5498b99..d4d9bb8e1 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -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", diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 7b20892a7..be2f3d34b 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -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 CreateAudioSystem(cpu::Processor* processor) { } std::unique_ptr CreateGraphicsSystem() { - if (FLAGS_gpu.compare("gl4") == 0) { - return std::unique_ptr( - new xe::gpu::gl4::GL4GraphicsSystem()); - } else if (FLAGS_gpu.compare("vulkan") == 0) { + if (FLAGS_gpu.compare("vulkan") == 0) { return std::unique_ptr( new xe::gpu::vulkan::VulkanGraphicsSystem()); } else if (FLAGS_gpu.compare("null") == 0) { diff --git a/src/xenia/gpu/gl4/draw_batcher.cc b/src/xenia/gpu/gl4/draw_batcher.cc deleted file mode 100644 index fa2368884..000000000 --- a/src/xenia/gpu/gl4/draw_batcher.cc +++ /dev/null @@ -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 - -#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(active_draw_.command_allocation.host_ptr); - auto state_host_ptr = - reinterpret_cast(active_draw_.state_allocation.host_ptr); - active_draw_.header = reinterpret_cast(state_host_ptr); - active_draw_.header->ps_param_gen = -1; - // active_draw_.float_consts = - // reinterpret_cast(state_host_ptr + - // batch_state_.float_consts_offset); - // active_draw_.bool_consts = - // reinterpret_cast(state_host_ptr + - // batch_state_.bool_consts_offset); - // active_draw_.loop_consts = - // reinterpret_cast(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(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( - 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 diff --git a/src/xenia/gpu/gl4/draw_batcher.h b/src/xenia/gpu/gl4/draw_batcher.h deleted file mode 100644 index fdecfb9da..000000000 --- a/src/xenia/gpu/gl4/draw_batcher.h +++ /dev/null @@ -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(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_ diff --git a/src/xenia/gpu/gl4/gl4_command_processor.cc b/src/xenia/gpu/gl4/gl4_command_processor.cc deleted file mode 100644 index 5392c22d2..000000000 --- a/src/xenia/gpu/gl4/gl4_command_processor.cc +++ /dev/null @@ -1,2131 +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_command_processor.h" - -#include - -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/profiling.h" -#include "xenia/gpu/gl4/gl4_gpu_flags.h" -#include "xenia/gpu/gl4/gl4_graphics_system.h" -#include "xenia/gpu/gpu_flags.h" -#include "xenia/gpu/sampler_info.h" -#include "xenia/gpu/texture_info.h" -#include "xenia/gpu/xenos.h" - -#include "third_party/xxhash/xxhash.h" - -DEFINE_bool(draw_all_framebuffers, false, - "Copy all render targets to screen on swap"); - -namespace xe { -namespace gpu { -namespace gl4 { - -using namespace xe::gpu::xenos; - -const GLuint kAnyTarget = UINT_MAX; - -// All uncached vertex/index data goes here. If it fills up we need to sync -// with the GPU, so this should be large enough to prevent that in a normal -// frame. -const size_t kScratchBufferCapacity = 256 * 1024 * 1024; -const size_t kScratchBufferAlignment = 256; - -GL4CommandProcessor::CachedPipeline::CachedPipeline() - : vertex_program(0), fragment_program(0), handles({0}) {} - -GL4CommandProcessor::CachedPipeline::~CachedPipeline() { - glDeleteProgramPipelines(1, &handles.default_pipeline); - glDeleteProgramPipelines(1, &handles.point_list_pipeline); - glDeleteProgramPipelines(1, &handles.rect_list_pipeline); - glDeleteProgramPipelines(1, &handles.quad_list_pipeline); - glDeleteProgramPipelines(1, &handles.line_quad_list_pipeline); -} - -GL4CommandProcessor::GL4CommandProcessor(GL4GraphicsSystem* graphics_system, - kernel::KernelState* kernel_state) - : CommandProcessor(graphics_system, kernel_state), - shader_translator_(GlslShaderTranslator::Dialect::kGL45), - draw_batcher_(graphics_system_->register_file()), - scratch_buffer_(kScratchBufferCapacity, kScratchBufferAlignment), - shader_cache_(&shader_translator_) {} - -GL4CommandProcessor::~GL4CommandProcessor() = default; - -void GL4CommandProcessor::ClearCaches() { - texture_cache()->Clear(); - - for (auto& cached_framebuffer : cached_framebuffers_) { - glDeleteFramebuffers(1, &cached_framebuffer.framebuffer); - } - cached_framebuffers_.clear(); - - for (auto& cached_color_render_target : cached_color_render_targets_) { - glDeleteTextures(1, &cached_color_render_target.texture); - } - cached_color_render_targets_.clear(); - - for (auto& cached_depth_render_target : cached_depth_render_targets_) { - glDeleteTextures(1, &cached_depth_render_target.texture); - } - cached_depth_render_targets_.clear(); - - CommandProcessor::ClearCaches(); -} - -bool GL4CommandProcessor::SetupContext() { - if (!CommandProcessor::SetupContext()) { - XELOGE("Unable to initialize base command processor context"); - return false; - } - - // Circular buffer holding scratch vertex/index data. - if (!scratch_buffer_.Initialize()) { - XELOGE("Unable to initialize scratch buffer"); - return false; - } - - // Command buffer. - if (!draw_batcher_.Initialize(&scratch_buffer_)) { - XELOGE("Unable to initialize command buffer"); - return false; - } - - // Texture cache that keeps track of any textures/samplers used. - if (!texture_cache_.Initialize(memory_, &scratch_buffer_)) { - XELOGE("Unable to initialize texture cache"); - return false; - } - - const std::string geometry_header = - "#version 450\n" - "#extension all : warn\n" - "#extension GL_ARB_explicit_uniform_location : require\n" - "#extension GL_ARB_shading_language_420pack : require\n" - "in gl_PerVertex {\n" - " vec4 gl_Position;\n" - " float gl_PointSize;\n" - " float gl_ClipDistance[];\n" - "} gl_in[];\n" - "out gl_PerVertex {\n" - " vec4 gl_Position;\n" - " float gl_PointSize;\n" - " float gl_ClipDistance[];\n" - "};\n" - "struct VertexData {\n" - " vec4 o[16];\n" - "};\n" - "\n" - "layout(location = 1) in VertexData in_vtx[];\n" - "layout(location = 1) out VertexData out_vtx;\n"; - // TODO(benvanik): fetch default point size from register and use that if - // the VS doesn't write oPointSize. - // TODO(benvanik): clamp to min/max. - // TODO(benvanik): figure out how to see which interpolator gets adjusted. - std::string point_list_shader = - geometry_header + - "layout(points) in;\n" - "layout(triangle_strip, max_vertices = 4) out;\n" - "void main() {\n" - " const vec2 offsets[4] = {\n" - " vec2(-1.0, 1.0),\n" - " vec2( 1.0, 1.0),\n" - " vec2(-1.0, -1.0),\n" - " vec2( 1.0, -1.0),\n" - " };\n" - " vec4 pos = gl_in[0].gl_Position;\n" - " float psize = gl_in[0].gl_PointSize;\n" - " for (int i = 0; i < 4; ++i) {\n" - " gl_Position = vec4(pos.xy + offsets[i] * psize, pos.zw);\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " }\n" - " EndPrimitive();\n" - "}\n"; - std::string rect_list_shader = - geometry_header + - "layout(triangles) in;\n" - "layout(triangle_strip, max_vertices = 6) out;\n" - "void main() {\n" - // Most games use the left-aligned form. - " bool left_aligned = gl_in[0].gl_Position.x == \n" - " gl_in[2].gl_Position.x;\n" - " if (left_aligned) {\n" - // 0 ------ 1 - // | - | - // | // | - // | - | - // 2 ----- [3] - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = \n" - " (gl_in[1].gl_Position + gl_in[2].gl_Position) - \n" - " gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " for (int i = 0; i < 16; ++i) {\n" - " out_vtx.o[i] = -in_vtx[0].o[i] + in_vtx[1].o[i] + \n" - " in_vtx[2].o[i];\n" - " }\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " } else {\n" - // 0 ------ 1 - // | - | - // | \\ | - // | - | - // [3] ----- 2 - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " gl_Position = (gl_in[0].gl_Position + gl_in[2].gl_Position) - \n" - " gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " for (int i = 0; i < 16; ++i) {\n" - " out_vtx.o[i] = in_vtx[0].o[i] + -in_vtx[1].o[i] + \n" - " in_vtx[2].o[i];\n" - " }\n" - " EmitVertex();\n" - " EndPrimitive();\n" - " }\n" - "}\n"; - std::string quad_list_shader = - geometry_header + - "layout(lines_adjacency) in;\n" - "layout(triangle_strip, max_vertices = 4) out;\n" - "void main() {\n" - " const int order[4] = { 0, 1, 3, 2 };\n" - " for (int i = 0; i < 4; ++i) {\n" - " int input_index = order[i];\n" - " gl_Position = gl_in[input_index].gl_Position;\n" - " gl_PointSize = gl_in[input_index].gl_PointSize;\n" - " out_vtx = in_vtx[input_index];\n" - " EmitVertex();\n" - " }\n" - " EndPrimitive();\n" - "}\n"; - std::string line_quad_list_shader = - geometry_header + - "layout(lines_adjacency) in;\n" - "layout(line_strip, max_vertices = 5) out;\n" - "void main() {\n" - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " gl_Position = gl_in[1].gl_Position;\n" - " gl_PointSize = gl_in[1].gl_PointSize;\n" - " out_vtx = in_vtx[1];\n" - " EmitVertex();\n" - " gl_Position = gl_in[2].gl_Position;\n" - " gl_PointSize = gl_in[2].gl_PointSize;\n" - " out_vtx = in_vtx[2];\n" - " EmitVertex();\n" - " gl_Position = gl_in[3].gl_Position;\n" - " gl_PointSize = gl_in[3].gl_PointSize;\n" - " out_vtx = in_vtx[3];\n" - " EmitVertex();\n" - " gl_Position = gl_in[0].gl_Position;\n" - " gl_PointSize = gl_in[0].gl_PointSize;\n" - " out_vtx = in_vtx[0];\n" - " EmitVertex();\n" - " EndPrimitive();\n" - "}\n"; - point_list_geometry_program_ = CreateGeometryProgram(point_list_shader); - rect_list_geometry_program_ = CreateGeometryProgram(rect_list_shader); - quad_list_geometry_program_ = CreateGeometryProgram(quad_list_shader); - line_quad_list_geometry_program_ = - CreateGeometryProgram(line_quad_list_shader); - if (!point_list_geometry_program_ || !rect_list_geometry_program_ || - !quad_list_geometry_program_ || !line_quad_list_geometry_program_) { - return false; - } - - glEnable(GL_SCISSOR_TEST); - glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); - glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_UPPER_LEFT); - - return true; -} - -GLuint GL4CommandProcessor::CreateGeometryProgram(const std::string& source) { - auto source_str = source.c_str(); - GLuint program = glCreateShaderProgramv(GL_GEOMETRY_SHADER, 1, &source_str); - - // Get error log, if we failed to link. - GLint link_status = 0; - glGetProgramiv(program, GL_LINK_STATUS, &link_status); - if (!link_status) { - GLint log_length = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); - std::string info_log; - info_log.resize(log_length - 1); - glGetProgramInfoLog(program, log_length, &log_length, - const_cast(info_log.data())); - XELOGE("Unable to link program: %s", info_log.c_str()); - glDeleteProgram(program); - return 0; - } - - return program; -} - -void GL4CommandProcessor::ShutdownContext() { - glDeleteProgram(point_list_geometry_program_); - glDeleteProgram(rect_list_geometry_program_); - glDeleteProgram(quad_list_geometry_program_); - glDeleteProgram(line_quad_list_geometry_program_); - texture_cache_.Shutdown(); - draw_batcher_.Shutdown(); - scratch_buffer_.Shutdown(); - - all_pipelines_.clear(); - shader_cache_.Reset(); - - CommandProcessor::ShutdownContext(); -} - -void GL4CommandProcessor::MakeCoherent() { - RegisterFile* regs = register_file_; - auto status_host = regs->values[XE_GPU_REG_COHER_STATUS_HOST].u32; - - CommandProcessor::MakeCoherent(); - - if (status_host & 0x80000000ul) { - scratch_buffer_.ClearCache(); - } -} - -void GL4CommandProcessor::PrepareForWait() { - SCOPE_profile_cpu_f("gpu"); - - CommandProcessor::PrepareForWait(); - - // TODO(benvanik): fences and fancy stuff. We should figure out a way to - // make interrupt callbacks from the GPU so that we don't have to do a full - // synchronize here. - glFlush(); - // glFinish(); - - if (FLAGS_thread_safe_gl) { - context_->ClearCurrent(); - } -} - -void GL4CommandProcessor::ReturnFromWait() { - if (FLAGS_thread_safe_gl) { - context_->MakeCurrent(); - } - - CommandProcessor::ReturnFromWait(); -} - -void GL4CommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, - uint32_t frontbuffer_width, - uint32_t frontbuffer_height) { - // Ensure we issue any pending draws. - draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); - - // One-time initialization. - // TODO(benvanik): move someplace more sane? - if (!swap_state_.front_buffer_texture) { - std::lock_guard lock(swap_state_.mutex); - swap_state_.width = frontbuffer_width; - swap_state_.height = frontbuffer_height; - GLuint front_buffer_texture; - GLuint back_buffer_texture; - glCreateTextures(GL_TEXTURE_2D, 1, &front_buffer_texture); - glCreateTextures(GL_TEXTURE_2D, 1, &back_buffer_texture); - swap_state_.front_buffer_texture = front_buffer_texture; - swap_state_.back_buffer_texture = back_buffer_texture; - glTextureStorage2D(front_buffer_texture, 1, GL_RGBA8, swap_state_.width, - swap_state_.height); - glTextureStorage2D(back_buffer_texture, 1, GL_RGBA8, swap_state_.width, - swap_state_.height); - } - - // Lookup the framebuffer in the recently-resolved list. - // TODO(benvanik): make this much more sophisticated. - // TODO(benvanik): handle not found cases. - // TODO(benvanik): handle dirty cases (resolved to sysmem, touched). - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // HACK: just use whatever our current framebuffer is. - GLuint framebuffer_texture = last_framebuffer_texture_; - - if (last_framebuffer_texture_ == 0) { - framebuffer_texture = - active_framebuffer_ ? active_framebuffer_->color_targets[0] : 0; - } - - // Copy the the given framebuffer to the current backbuffer. - Rect2D src_rect(0, 0, frontbuffer_width ? frontbuffer_width : 1280, - frontbuffer_height ? frontbuffer_height : 720); - Rect2D dest_rect(0, 0, swap_state_.width, swap_state_.height); - if (framebuffer_texture != 0) { - reinterpret_cast(context_.get()) - ->blitter() - ->CopyColorTexture2D( - framebuffer_texture, src_rect, - static_cast(swap_state_.back_buffer_texture), dest_rect, - GL_LINEAR, true); - } - - if (FLAGS_draw_all_framebuffers) { - int32_t offsetx = (1280 - (1280 / 5)); - int32_t offsety = 0; - int32_t doffsetx = 0; - for (int i = 0; i < cached_framebuffers_.size(); i++) { - bool has_colortargets = false; - - // Copy color targets to top right corner - for (int j = 0; j < 4; j++) { - GLuint tex = cached_framebuffers_[i].color_targets[j]; - if (!tex) { - continue; - } - has_colortargets = true; - - dest_rect = {offsetx, offsety, 1280 / 5, 720 / 5}; - reinterpret_cast(context_.get()) - ->blitter() - ->CopyColorTexture2D( - tex, src_rect, - static_cast(swap_state_.back_buffer_texture), dest_rect, - GL_LINEAR, true); - - offsety += 720 / 5; - } - - if (has_colortargets) { - offsetx -= 1280 / 5; - } - - offsety = 0; - - GLuint tex = cached_framebuffers_[i].depth_target; - if (!tex) { - continue; - } - - // Copy depth targets to bottom left corner of screen - dest_rect = {doffsetx, (int32_t)swap_state_.height - (720 / 5), 1280 / 5, - 720 / 5}; - reinterpret_cast(context_.get()) - ->blitter() - ->CopyColorTexture2D( - tex, src_rect, - static_cast(swap_state_.back_buffer_texture), dest_rect, - GL_LINEAR, false); - - doffsetx += 1280 / 5; - } - } - - // Need to finish to be sure the other context sees the right data. - // TODO(benvanik): prevent this? fences? - glFinish(); - - if (context_->WasLost()) { - // We've lost the context due to a TDR. - // TODO: Dump the current commands to a tracefile. - assert_always(); - } - - // Remove any dead textures, etc. - texture_cache_.Scavenge(); -} - -Shader* GL4CommandProcessor::LoadShader(ShaderType shader_type, - uint32_t guest_address, - const uint32_t* host_address, - uint32_t dword_count) { - return shader_cache_.LookupOrInsertShader(shader_type, host_address, - dword_count); -} - -bool GL4CommandProcessor::IssueDraw(PrimitiveType prim_type, - uint32_t index_count, - IndexBufferInfo* index_buffer_info) { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - bool draw_valid; - if (index_buffer_info) { - draw_valid = draw_batcher_.BeginDrawElements(prim_type, index_count, - index_buffer_info->format); - } else { - draw_valid = draw_batcher_.BeginDrawArrays(prim_type, index_count); - } - if (!draw_valid) { - return false; - } - - auto& regs = *register_file_; - - auto enable_mode = - static_cast(regs[XE_GPU_REG_RB_MODECONTROL].u32 & 0x7); - if (enable_mode == ModeControl::kIgnore) { - // Ignored. - draw_batcher_.DiscardDraw(); - return true; - } else if (enable_mode == ModeControl::kCopy) { - // Special copy handling. - draw_batcher_.DiscardDraw(); - return IssueCopy(); - } - -#define CHECK_ISSUE_UPDATE_STATUS(status, mismatch, error_message) \ - { \ - if (status == UpdateStatus::kError) { \ - XELOGE(error_message); \ - draw_batcher_.DiscardDraw(); \ - return false; \ - } else if (status == UpdateStatus::kMismatch) { \ - mismatch = true; \ - } \ - } - - UpdateStatus status; - bool mismatch = false; - status = UpdateShaders(draw_batcher_.prim_type()); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to prepare draw shaders"); - status = UpdateRenderTargets(); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render targets"); - if (!active_framebuffer_) { - // No framebuffer, so nothing we do will actually have an effect. - // Treat it as a no-op. - // TODO(benvanik): if we have a vs export, still allow it to go. - draw_batcher_.DiscardDraw(); - return true; - } - - status = UpdateState(draw_batcher_.prim_type()); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render state"); - status = PopulateSamplers(); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, - "Unable to prepare draw samplers"); - - status = PopulateIndexBuffer(index_buffer_info); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup index buffer"); - status = PopulateVertexBuffers(); - CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup vertex buffers"); - - if (!draw_batcher_.CommitDraw()) { - return false; - } - - // TODO(benvanik): find a way to get around glVertexArrayVertexBuffer below. - draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); - if (context_->WasLost()) { - // This draw lost us the context. This typically isn't hit. - assert_always(); - return false; - } - - return true; -} - -bool GL4CommandProcessor::SetShadowRegister(uint32_t* dest, - uint32_t register_name) { - uint32_t value = register_file_->values[register_name].u32; - if (*dest == value) { - return false; - } - *dest = value; - return true; -} - -bool GL4CommandProcessor::SetShadowRegister(float* dest, - uint32_t register_name) { - float value = register_file_->values[register_name].f32; - if (*dest == value) { - return false; - } - *dest = value; - return true; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateShaders( - PrimitiveType prim_type) { - auto& regs = update_shaders_regs_; - - // These are the constant base addresses/ranges for shaders. - // We have these hardcoded right now cause nothing seems to differ. - assert_true(register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == - 0x000FF000 || - register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == 0x00000000); - assert_true(register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == - 0x000FF100 || - register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == 0x00000000); - - bool dirty = false; - dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, - XE_GPU_REG_PA_SU_SC_MODE_CNTL); - dirty |= SetShadowRegister(®s.sq_program_cntl, XE_GPU_REG_SQ_PROGRAM_CNTL); - dirty |= SetShadowRegister(®s.sq_context_misc, XE_GPU_REG_SQ_CONTEXT_MISC); - dirty |= regs.vertex_shader != active_vertex_shader_; - dirty |= regs.pixel_shader != active_pixel_shader_; - dirty |= regs.prim_type != prim_type; - if (!dirty) { - return UpdateStatus::kCompatible; - } - regs.vertex_shader = static_cast(active_vertex_shader_); - regs.pixel_shader = static_cast(active_pixel_shader_); - regs.prim_type = prim_type; - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - xe_gpu_program_cntl_t program_cntl; - program_cntl.dword_0 = regs.sq_program_cntl; - - // Populate a register in the pixel shader with frag coord. - int ps_param_gen = (regs.sq_context_misc >> 8) & 0xFF; - draw_batcher_.set_ps_param_gen(program_cntl.param_gen ? ps_param_gen : -1); - - // Normal vertex shaders only, for now. - // TODO(benvanik): transform feedback/memexport. - // https://github.com/freedreno/freedreno/blob/master/includes/a2xx.xml.h - // 0 = normal - // 2 = point size - assert_true(program_cntl.vs_export_mode == 0 || - program_cntl.vs_export_mode == 2); - - if (!regs.vertex_shader->is_valid()) { - XELOGE("Vertex shader invalid"); - return UpdateStatus::kError; - } - if (!regs.pixel_shader->is_valid()) { - XELOGE("Pixel shader invalid"); - return UpdateStatus::kError; - } - - GLuint vertex_program = regs.vertex_shader->program(); - GLuint fragment_program = regs.pixel_shader->program(); - - uint64_t key = (uint64_t(vertex_program) << 32) | fragment_program; - CachedPipeline* cached_pipeline = nullptr; - auto it = cached_pipelines_.find(key); - if (it == cached_pipelines_.end()) { - // Existing pipeline for these programs not found - create it. - auto new_pipeline = std::make_unique(); - new_pipeline->vertex_program = vertex_program; - new_pipeline->fragment_program = fragment_program; - new_pipeline->handles.default_pipeline = 0; - cached_pipeline = new_pipeline.get(); - all_pipelines_.emplace_back(std::move(new_pipeline)); - cached_pipelines_.insert({key, cached_pipeline}); - } else { - // Found a pipeline container - it may or may not have what we want. - cached_pipeline = it->second; - } - if (!cached_pipeline->handles.default_pipeline) { - // Perhaps it's a bit wasteful to do all of these, but oh well. - GLuint pipelines[5]; - glCreateProgramPipelines(GLsizei(xe::countof(pipelines)), pipelines); - - glUseProgramStages(pipelines[0], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[0], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.default_pipeline = pipelines[0]; - - glUseProgramStages(pipelines[1], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[1], GL_GEOMETRY_SHADER_BIT, - point_list_geometry_program_); - glUseProgramStages(pipelines[1], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.point_list_pipeline = pipelines[1]; - - glUseProgramStages(pipelines[2], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[2], GL_GEOMETRY_SHADER_BIT, - rect_list_geometry_program_); - glUseProgramStages(pipelines[2], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.rect_list_pipeline = pipelines[2]; - - glUseProgramStages(pipelines[3], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[3], GL_GEOMETRY_SHADER_BIT, - quad_list_geometry_program_); - glUseProgramStages(pipelines[3], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.quad_list_pipeline = pipelines[3]; - - glUseProgramStages(pipelines[4], GL_VERTEX_SHADER_BIT, vertex_program); - glUseProgramStages(pipelines[4], GL_GEOMETRY_SHADER_BIT, - line_quad_list_geometry_program_); - glUseProgramStages(pipelines[4], GL_FRAGMENT_SHADER_BIT, fragment_program); - cached_pipeline->handles.line_quad_list_pipeline = pipelines[4]; - - // This can be set once, as the buffer never changes. - glVertexArrayElementBuffer(regs.vertex_shader->vao(), - scratch_buffer_.handle()); - } - - bool line_mode = false; - if (((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0) { - uint32_t front_poly_mode = (regs.pa_su_sc_mode_cntl >> 5) & 0x7; - if (front_poly_mode == 1) { - line_mode = true; - } - } - - GLuint pipeline; - switch (regs.prim_type) { - default: - // Default pipeline used. - pipeline = cached_pipeline->handles.default_pipeline; - break; - case PrimitiveType::kPointList: - pipeline = cached_pipeline->handles.point_list_pipeline; - break; - case PrimitiveType::kRectangleList: - pipeline = cached_pipeline->handles.rect_list_pipeline; - break; - case PrimitiveType::kQuadList: { - if (line_mode) { - pipeline = cached_pipeline->handles.line_quad_list_pipeline; - } else { - pipeline = cached_pipeline->handles.quad_list_pipeline; - } - break; - } - } - - draw_batcher_.ReconfigurePipeline(regs.vertex_shader, regs.pixel_shader, - pipeline); - - glBindProgramPipeline(pipeline); - glBindVertexArray(regs.vertex_shader->vao()); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() { - auto& regs = update_render_targets_regs_; - - bool dirty = false; - dirty |= SetShadowRegister(®s.rb_modecontrol, XE_GPU_REG_RB_MODECONTROL); - dirty |= SetShadowRegister(®s.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO); - dirty |= SetShadowRegister(®s.rb_color_info, XE_GPU_REG_RB_COLOR_INFO); - dirty |= SetShadowRegister(®s.rb_color1_info, XE_GPU_REG_RB_COLOR1_INFO); - dirty |= SetShadowRegister(®s.rb_color2_info, XE_GPU_REG_RB_COLOR2_INFO); - dirty |= SetShadowRegister(®s.rb_color3_info, XE_GPU_REG_RB_COLOR3_INFO); - dirty |= SetShadowRegister(®s.rb_color_mask, XE_GPU_REG_RB_COLOR_MASK); - dirty |= SetShadowRegister(®s.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL); - dirty |= - SetShadowRegister(®s.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK); - dirty |= SetShadowRegister(®s.rb_depth_info, XE_GPU_REG_RB_DEPTH_INFO); - if (!dirty) { - return UpdateStatus::kCompatible; - } - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - auto enable_mode = static_cast(regs.rb_modecontrol & 0x7); - - // RB_SURFACE_INFO - // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html - uint32_t surface_pitch = regs.rb_surface_info & 0x3FFF; - auto surface_msaa = - static_cast((regs.rb_surface_info >> 16) & 0x3); - - // Get/create all color render targets, if we are using them. - // In depth-only mode we don't need them. - // Note that write mask may be more permissive than we want, so we mix that - // with the actual targets the pixel shader writes to. - GLenum draw_buffers[4] = {GL_NONE, GL_NONE, GL_NONE, GL_NONE}; - GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget}; - if (enable_mode == ModeControl::kColorDepth) { - uint32_t color_info[4] = { - regs.rb_color_info, - regs.rb_color1_info, - regs.rb_color2_info, - regs.rb_color3_info, - }; - // A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE - for (int n = 0; n < xe::countof(color_info); n++) { - uint32_t write_mask = (regs.rb_color_mask >> (n * 4)) & 0xF; - if (!write_mask || !active_pixel_shader_->writes_color_target(n)) { - // Unused, so keep disabled and set to wildcard so we'll take any - // framebuffer that has it. - continue; - } - uint32_t color_base = color_info[n] & 0xFFF; - auto color_format = - static_cast((color_info[n] >> 16) & 0xF); - color_targets[n] = GetColorRenderTarget(surface_pitch, surface_msaa, - color_base, color_format); - draw_buffers[n] = GL_COLOR_ATTACHMENT0 + n; - glColorMaski(n, !!(write_mask & 0x1), !!(write_mask & 0x2), - !!(write_mask & 0x4), !!(write_mask & 0x8)); - } - } - - // Get/create depth buffer, but only if we are going to use it. - bool uses_depth = (regs.rb_depthcontrol & 0x00000002) || - (regs.rb_depthcontrol & 0x00000004); - uint32_t stencil_write_mask = (regs.rb_stencilrefmask & 0x00FF0000) >> 16; - bool uses_stencil = - (regs.rb_depthcontrol & 0x00000001) || (stencil_write_mask != 0); - GLuint depth_target = kAnyTarget; - if (uses_depth || uses_stencil) { - uint32_t depth_base = regs.rb_depth_info & 0xFFF; - auto depth_format = - static_cast((regs.rb_depth_info >> 16) & 0x1); - depth_target = GetDepthRenderTarget(surface_pitch, surface_msaa, depth_base, - depth_format); - // TODO(benvanik): when a game switches does it expect to keep the same - // depth buffer contents? - } - - // Get/create a framebuffer with the required targets. - // Note that none may be returned if we really don't need one. - auto cached_framebuffer = GetFramebuffer(color_targets, depth_target); - active_framebuffer_ = cached_framebuffer; - if (active_framebuffer_) { - // Setup just the targets we want. - glNamedFramebufferDrawBuffers(cached_framebuffer->framebuffer, 4, - draw_buffers); - - // Make active. - // TODO(benvanik): can we do this all named? - // TODO(benvanik): do we want this on READ too? - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, cached_framebuffer->framebuffer); - } - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateState( - PrimitiveType prim_type) { - bool mismatch = false; - -#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \ - { \ - if (status == UpdateStatus::kError) { \ - XELOGE(error_message); \ - return status; \ - } else if (status == UpdateStatus::kMismatch) { \ - mismatch = true; \ - } \ - } - - UpdateStatus status; - status = UpdateViewportState(); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update viewport state"); - status = UpdateRasterizerState(prim_type); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update rasterizer state"); - status = UpdateBlendState(); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update blend state"); - status = UpdateDepthStencilState(); - CHECK_UPDATE_STATUS(status, mismatch, "Unable to update depth/stencil state"); - - return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateViewportState() { - auto& regs = update_viewport_state_regs_; - - bool dirty = false; - // dirty |= SetShadowRegister(&state_regs.pa_cl_clip_cntl, - // XE_GPU_REG_PA_CL_CLIP_CNTL); - dirty |= SetShadowRegister(®s.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO); - dirty |= SetShadowRegister(®s.pa_cl_vte_cntl, XE_GPU_REG_PA_CL_VTE_CNTL); - dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, - XE_GPU_REG_PA_SU_SC_MODE_CNTL); - dirty |= SetShadowRegister(®s.pa_sc_window_offset, - XE_GPU_REG_PA_SC_WINDOW_OFFSET); - dirty |= SetShadowRegister(®s.pa_sc_window_scissor_tl, - XE_GPU_REG_PA_SC_WINDOW_SCISSOR_TL); - dirty |= SetShadowRegister(®s.pa_sc_window_scissor_br, - XE_GPU_REG_PA_SC_WINDOW_SCISSOR_BR); - dirty |= SetShadowRegister(®s.pa_cl_vport_xoffset, - XE_GPU_REG_PA_CL_VPORT_XOFFSET); - dirty |= SetShadowRegister(®s.pa_cl_vport_yoffset, - XE_GPU_REG_PA_CL_VPORT_YOFFSET); - dirty |= SetShadowRegister(®s.pa_cl_vport_zoffset, - XE_GPU_REG_PA_CL_VPORT_ZOFFSET); - dirty |= SetShadowRegister(®s.pa_cl_vport_xscale, - XE_GPU_REG_PA_CL_VPORT_XSCALE); - dirty |= SetShadowRegister(®s.pa_cl_vport_yscale, - XE_GPU_REG_PA_CL_VPORT_YSCALE); - dirty |= SetShadowRegister(®s.pa_cl_vport_zscale, - XE_GPU_REG_PA_CL_VPORT_ZSCALE); - - // Much of this state machine is extracted from: - // https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c - // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html - // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf - - // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf - // VTX_XY_FMT = true: the incoming X, Y have already been multiplied by 1/W0. - // = false: multiply the X, Y coordinates by 1/W0. - // VTX_Z_FMT = true: the incoming Z has already been multiplied by 1/W0. - // = false: multiply the Z coordinate by 1/W0. - // VTX_W0_FMT = true: the incoming W0 is not 1/W0. Perform the reciprocal to - // get 1/W0. - draw_batcher_.set_vtx_fmt((regs.pa_cl_vte_cntl >> 8) & 0x1 ? 1.0f : 0.0f, - (regs.pa_cl_vte_cntl >> 9) & 0x1 ? 1.0f : 0.0f, - (regs.pa_cl_vte_cntl >> 10) & 0x1 ? 1.0f : 0.0f); - - // Done in VS, no need to flush state. - if ((regs.pa_cl_vte_cntl & (1 << 0)) > 0) { - draw_batcher_.set_window_scalar(1.0f, 1.0f); - } else { - draw_batcher_.set_window_scalar(1.0f / 2560.0f, -1.0f / 2560.0f); - } - - if (!dirty) { - return UpdateStatus::kCompatible; - } - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - // Clipping. - // https://github.com/freedreno/amd-gpu/blob/master/include/reg/yamato/14/yamato_genenum.h#L1587 - // bool clip_enabled = ((regs.pa_cl_clip_cntl >> 17) & 0x1) == 0; - // bool dx_clip = ((regs.pa_cl_clip_cntl >> 19) & 0x1) == 0x1; - //// TODO(benvanik): depth range? - // if (dx_clip) { - // glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); - //} else { - // glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE); - //} - - // Window parameters. - // http://ftp.tku.edu.tw/NetBSD/NetBSD-current/xsrc/external/mit/xf86-video-ati/dist/src/r600_reg_auto_r6xx.h - // See r200UpdateWindow: - // https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c - int16_t window_offset_x = 0; - int16_t window_offset_y = 0; - if ((regs.pa_su_sc_mode_cntl >> 16) & 1) { - window_offset_x = regs.pa_sc_window_offset & 0x7FFF; - window_offset_y = (regs.pa_sc_window_offset >> 16) & 0x7FFF; - if (window_offset_x & 0x4000) { - window_offset_x |= 0x8000; - } - if (window_offset_y & 0x4000) { - window_offset_y |= 0x8000; - } - } - - GLint ws_x = regs.pa_sc_window_scissor_tl & 0x7FFF; - GLint ws_y = (regs.pa_sc_window_scissor_tl >> 16) & 0x7FFF; - GLsizei ws_w = (regs.pa_sc_window_scissor_br & 0x7FFF) - ws_x; - GLsizei ws_h = ((regs.pa_sc_window_scissor_br >> 16) & 0x7FFF) - ws_y; - ws_x += window_offset_x; - ws_y += window_offset_y; - glScissorIndexed(0, ws_x, ws_y, ws_w, ws_h); - - // HACK: no clue where to get these values. - // RB_SURFACE_INFO - auto surface_msaa = - static_cast((regs.rb_surface_info >> 16) & 0x3); - // TODO(benvanik): ?? - float window_width_scalar = 1; - float window_height_scalar = 1; - switch (surface_msaa) { - case MsaaSamples::k1X: - break; - case MsaaSamples::k2X: - window_width_scalar = 2; - break; - case MsaaSamples::k4X: - window_width_scalar = 2; - window_height_scalar = 2; - break; - } - - // Whether each of the viewport settings are enabled. - // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf - bool vport_xscale_enable = (regs.pa_cl_vte_cntl & (1 << 0)) > 0; - bool vport_xoffset_enable = (regs.pa_cl_vte_cntl & (1 << 1)) > 0; - bool vport_yscale_enable = (regs.pa_cl_vte_cntl & (1 << 2)) > 0; - bool vport_yoffset_enable = (regs.pa_cl_vte_cntl & (1 << 3)) > 0; - bool vport_zscale_enable = (regs.pa_cl_vte_cntl & (1 << 4)) > 0; - bool vport_zoffset_enable = (regs.pa_cl_vte_cntl & (1 << 5)) > 0; - assert_true(vport_xscale_enable == vport_yscale_enable == - vport_zscale_enable == vport_xoffset_enable == - vport_yoffset_enable == vport_zoffset_enable); - - if (vport_xscale_enable) { - float texel_offset_x = 0.0f; - float texel_offset_y = 0.0f; - float vox = vport_xoffset_enable ? regs.pa_cl_vport_xoffset : 0; - float voy = vport_yoffset_enable ? regs.pa_cl_vport_yoffset : 0; - float vsx = vport_xscale_enable ? regs.pa_cl_vport_xscale : 1; - float vsy = vport_yscale_enable ? regs.pa_cl_vport_yscale : 1; - window_width_scalar = window_height_scalar = 1; - float vpw = 2 * window_width_scalar * vsx; - float vph = -2 * window_height_scalar * vsy; - float vpx = window_width_scalar * vox - vpw / 2 + window_offset_x; - float vpy = window_height_scalar * voy - vph / 2 + window_offset_y; - glViewportIndexedf(0, vpx + texel_offset_x, vpy + texel_offset_y, vpw, vph); - - // TODO(benvanik): depth range adjustment? - // float voz = vport_zoffset_enable ? regs.pa_cl_vport_zoffset : 0; - // float vsz = vport_zscale_enable ? regs.pa_cl_vport_zscale : 1; - } else { - float texel_offset_x = 0.0f; - float texel_offset_y = 0.0f; - float vpw = 2 * 2560.0f * window_width_scalar; - float vph = 2 * 2560.0f * window_height_scalar; - float vpx = -2560.0f * window_width_scalar + window_offset_x; - float vpy = -2560.0f * window_height_scalar + window_offset_y; - glViewportIndexedf(0, vpx + texel_offset_x, vpy + texel_offset_y, vpw, vph); - } - float voz = vport_zoffset_enable ? regs.pa_cl_vport_zoffset : 0; - float vsz = vport_zscale_enable ? regs.pa_cl_vport_zscale : 1; - glDepthRangef(voz, voz + vsz); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState( - PrimitiveType prim_type) { - auto& regs = update_rasterizer_state_regs_; - - bool dirty = false; - dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, - XE_GPU_REG_PA_SU_SC_MODE_CNTL); - dirty |= SetShadowRegister(®s.pa_sc_screen_scissor_tl, - XE_GPU_REG_PA_SC_SCREEN_SCISSOR_TL); - dirty |= SetShadowRegister(®s.pa_sc_screen_scissor_br, - XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR); - dirty |= SetShadowRegister(®s.multi_prim_ib_reset_index, - XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX); - dirty |= SetShadowRegister(®s.pa_sc_viz_query, XE_GPU_REG_PA_SC_VIZ_QUERY); - dirty |= regs.prim_type != prim_type; - if (!dirty) { - return UpdateStatus::kCompatible; - } - - regs.prim_type = prim_type; - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - // viz query enabled - // assert_zero(regs.pa_sc_viz_query & 0x01); - - // Kill pix post early-z test - // assert_zero(regs.pa_sc_viz_query & 0x80); - - // Scissoring. - // TODO(benvanik): is this used? we are using scissoring for window scissor. - if (regs.pa_sc_screen_scissor_tl != 0 && - regs.pa_sc_screen_scissor_br != 0x20002000) { - assert_always(); - // glEnable(GL_SCISSOR_TEST); - // TODO(benvanik): signed? - int32_t screen_scissor_x = regs.pa_sc_screen_scissor_tl & 0x7FFF; - int32_t screen_scissor_y = (regs.pa_sc_screen_scissor_tl >> 16) & 0x7FFF; - int32_t screen_scissor_w = - regs.pa_sc_screen_scissor_br & 0x7FFF - screen_scissor_x; - int32_t screen_scissor_h = - (regs.pa_sc_screen_scissor_br >> 16) & 0x7FFF - screen_scissor_y; - glScissor(screen_scissor_x, screen_scissor_y, screen_scissor_w, - screen_scissor_h); - } else { - // glDisable(GL_SCISSOR_TEST); - } - - switch (regs.pa_su_sc_mode_cntl & 0x3) { - case 0: - glDisable(GL_CULL_FACE); - break; - case 1: - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - break; - case 2: - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - break; - } - if (regs.pa_su_sc_mode_cntl & 0x4) { - glFrontFace(GL_CW); - } else { - glFrontFace(GL_CCW); - } - - if (prim_type == PrimitiveType::kRectangleList) { - // Rectangle lists aren't culled. There may be other things they skip too. - glDisable(GL_CULL_FACE); - } - - static const GLenum kFillModes[3] = { - GL_POINT, - GL_LINE, - GL_FILL, - }; - bool poly_mode = ((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0; - if (poly_mode) { - uint32_t front_poly_mode = (regs.pa_su_sc_mode_cntl >> 5) & 0x7; - uint32_t back_poly_mode = (regs.pa_su_sc_mode_cntl >> 8) & 0x7; - // GL only supports both matching. - assert_true(front_poly_mode == back_poly_mode); - glPolygonMode(GL_FRONT_AND_BACK, kFillModes[front_poly_mode]); - } else { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - - if (regs.pa_su_sc_mode_cntl & (1 << 19)) { - glProvokingVertex(GL_LAST_VERTEX_CONVENTION); - } else { - glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); - } - - if (regs.pa_su_sc_mode_cntl & (1 << 21)) { - glEnable(GL_PRIMITIVE_RESTART); - } else { - glDisable(GL_PRIMITIVE_RESTART); - } - glPrimitiveRestartIndex(regs.multi_prim_ib_reset_index); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateBlendState() { - auto& reg_file = *register_file_; - auto& regs = update_blend_state_regs_; - - // Alpha testing -- ALPHAREF, ALPHAFUNC, ALPHATESTENABLE - // Deprecated in GL, implemented in shader. - // if(ALPHATESTENABLE && frag_out.a [<=/ALPHAFUNC] ALPHAREF) discard; - uint32_t color_control = reg_file[XE_GPU_REG_RB_COLORCONTROL].u32; - draw_batcher_.set_alpha_test((color_control & 0x8) != 0, // ALPAHTESTENABLE - color_control & 0x7, // ALPHAFUNC - reg_file[XE_GPU_REG_RB_ALPHA_REF].f32); - - bool dirty = false; - dirty |= - SetShadowRegister(®s.rb_blendcontrol[0], XE_GPU_REG_RB_BLENDCONTROL_0); - dirty |= - SetShadowRegister(®s.rb_blendcontrol[1], XE_GPU_REG_RB_BLENDCONTROL_1); - dirty |= - SetShadowRegister(®s.rb_blendcontrol[2], XE_GPU_REG_RB_BLENDCONTROL_2); - dirty |= - SetShadowRegister(®s.rb_blendcontrol[3], XE_GPU_REG_RB_BLENDCONTROL_3); - dirty |= SetShadowRegister(®s.rb_blend_rgba[0], XE_GPU_REG_RB_BLEND_RED); - dirty |= SetShadowRegister(®s.rb_blend_rgba[1], XE_GPU_REG_RB_BLEND_GREEN); - dirty |= SetShadowRegister(®s.rb_blend_rgba[2], XE_GPU_REG_RB_BLEND_BLUE); - dirty |= SetShadowRegister(®s.rb_blend_rgba[3], XE_GPU_REG_RB_BLEND_ALPHA); - if (!dirty) { - return UpdateStatus::kCompatible; - } - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - static const GLenum blend_map[] = { - /* 0 */ GL_ZERO, - /* 1 */ GL_ONE, - /* 2 */ GL_ZERO, // ? - /* 3 */ GL_ZERO, // ? - /* 4 */ GL_SRC_COLOR, - /* 5 */ GL_ONE_MINUS_SRC_COLOR, - /* 6 */ GL_SRC_ALPHA, - /* 7 */ GL_ONE_MINUS_SRC_ALPHA, - /* 8 */ GL_DST_COLOR, - /* 9 */ GL_ONE_MINUS_DST_COLOR, - /* 10 */ GL_DST_ALPHA, - /* 11 */ GL_ONE_MINUS_DST_ALPHA, - /* 12 */ GL_CONSTANT_COLOR, - /* 13 */ GL_ONE_MINUS_CONSTANT_COLOR, - /* 14 */ GL_CONSTANT_ALPHA, - /* 15 */ GL_ONE_MINUS_CONSTANT_ALPHA, - /* 16 */ GL_SRC_ALPHA_SATURATE, - }; - static const GLenum blend_op_map[] = { - /* 0 */ GL_FUNC_ADD, - /* 1 */ GL_FUNC_SUBTRACT, - /* 2 */ GL_MIN, - /* 3 */ GL_MAX, - /* 4 */ GL_FUNC_REVERSE_SUBTRACT, - }; - for (int i = 0; i < xe::countof(regs.rb_blendcontrol); ++i) { - uint32_t blend_control = regs.rb_blendcontrol[i]; - // A2XX_RB_BLEND_CONTROL_COLOR_SRCBLEND - auto src_blend = blend_map[(blend_control & 0x0000001F) >> 0]; - // A2XX_RB_BLEND_CONTROL_COLOR_DESTBLEND - auto dest_blend = blend_map[(blend_control & 0x00001F00) >> 8]; - // A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN - auto blend_op = blend_op_map[(blend_control & 0x000000E0) >> 5]; - // A2XX_RB_BLEND_CONTROL_ALPHA_SRCBLEND - auto src_blend_alpha = blend_map[(blend_control & 0x001F0000) >> 16]; - // A2XX_RB_BLEND_CONTROL_ALPHA_DESTBLEND - auto dest_blend_alpha = blend_map[(blend_control & 0x1F000000) >> 24]; - // A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN - auto blend_op_alpha = blend_op_map[(blend_control & 0x00E00000) >> 21]; - // A2XX_RB_COLORCONTROL_BLEND_DISABLE ?? Can't find this! - // Just guess based on actions. - // bool blend_enable = - // !((src_blend == GL_ONE) && (dest_blend == GL_ZERO) && - // (blend_op == GL_FUNC_ADD) && (src_blend_alpha == GL_ONE) && - // (dest_blend_alpha == GL_ZERO) && (blend_op_alpha == GL_FUNC_ADD)); - bool blend_enable = !(color_control & 0x20); - if (blend_enable) { - glEnablei(GL_BLEND, i); - glBlendEquationSeparatei(i, blend_op, blend_op_alpha); - glBlendFuncSeparatei(i, src_blend, dest_blend, src_blend_alpha, - dest_blend_alpha); - } else { - glDisablei(GL_BLEND, i); - } - } - - glBlendColor(regs.rb_blend_rgba[0], regs.rb_blend_rgba[1], - regs.rb_blend_rgba[2], regs.rb_blend_rgba[3]); - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus -GL4CommandProcessor::UpdateDepthStencilState() { - auto& regs = update_depth_stencil_state_regs_; - - bool dirty = false; - dirty |= SetShadowRegister(®s.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL); - dirty |= - SetShadowRegister(®s.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK); - if (!dirty) { - return UpdateStatus::kCompatible; - } - - SCOPE_profile_cpu_f("gpu"); - - draw_batcher_.Flush(DrawBatcher::FlushMode::kStateChange); - - static const GLenum compare_func_map[] = { - /* 0 */ GL_NEVER, - /* 1 */ GL_LESS, - /* 2 */ GL_EQUAL, - /* 3 */ GL_LEQUAL, - /* 4 */ GL_GREATER, - /* 5 */ GL_NOTEQUAL, - /* 6 */ GL_GEQUAL, - /* 7 */ GL_ALWAYS, - }; - static const GLenum stencil_op_map[] = { - /* 0 */ GL_KEEP, - /* 1 */ GL_ZERO, - /* 2 */ GL_REPLACE, - /* 3 */ GL_INCR_WRAP, - /* 4 */ GL_DECR_WRAP, - /* 5 */ GL_INVERT, - /* 6 */ GL_INCR, - /* 7 */ GL_DECR, - }; - // A2XX_RB_DEPTHCONTROL_Z_ENABLE - if (regs.rb_depthcontrol & 0x00000002) { - glEnable(GL_DEPTH_TEST); - } else { - glDisable(GL_DEPTH_TEST); - } - // glDisable(GL_DEPTH_TEST); - // A2XX_RB_DEPTHCONTROL_Z_WRITE_ENABLE - glDepthMask((regs.rb_depthcontrol & 0x00000004) ? GL_TRUE : GL_FALSE); - // A2XX_RB_DEPTHCONTROL_EARLY_Z_ENABLE - // ? - // A2XX_RB_DEPTHCONTROL_ZFUNC - glDepthFunc(compare_func_map[(regs.rb_depthcontrol & 0x00000070) >> 4]); - // A2XX_RB_DEPTHCONTROL_STENCIL_ENABLE - if (regs.rb_depthcontrol & 0x00000001) { - glEnable(GL_STENCIL_TEST); - } else { - glDisable(GL_STENCIL_TEST); - } - // RB_STENCILREFMASK_STENCILREF - uint32_t stencil_ref = (regs.rb_stencilrefmask & 0x000000FF); - // RB_STENCILREFMASK_STENCILMASK - uint32_t stencil_read_mask = (regs.rb_stencilrefmask & 0x0000FF00) >> 8; - // RB_STENCILREFMASK_STENCILWRITEMASK - glStencilMask((regs.rb_stencilrefmask & 0x00FF0000) >> 16); - // A2XX_RB_DEPTHCONTROL_BACKFACE_ENABLE - bool backface_enabled = (regs.rb_depthcontrol & 0x00000080) != 0; - if (backface_enabled) { - // A2XX_RB_DEPTHCONTROL_STENCILFUNC - glStencilFuncSeparate( - GL_FRONT, compare_func_map[(regs.rb_depthcontrol & 0x00000700) >> 8], - stencil_ref, stencil_read_mask); - // A2XX_RB_DEPTHCONTROL_STENCILFAIL - // A2XX_RB_DEPTHCONTROL_STENCILZFAIL - // A2XX_RB_DEPTHCONTROL_STENCILZPASS - glStencilOpSeparate( - GL_FRONT, stencil_op_map[(regs.rb_depthcontrol & 0x00003800) >> 11], - stencil_op_map[(regs.rb_depthcontrol & 0x000E0000) >> 17], - stencil_op_map[(regs.rb_depthcontrol & 0x0001C000) >> 14]); - // A2XX_RB_DEPTHCONTROL_STENCILFUNC_BF - glStencilFuncSeparate( - GL_BACK, compare_func_map[(regs.rb_depthcontrol & 0x00700000) >> 20], - stencil_ref, stencil_read_mask); - // A2XX_RB_DEPTHCONTROL_STENCILFAIL_BF - // A2XX_RB_DEPTHCONTROL_STENCILZFAIL_BF - // A2XX_RB_DEPTHCONTROL_STENCILZPASS_BF - glStencilOpSeparate( - GL_BACK, stencil_op_map[(regs.rb_depthcontrol & 0x03800000) >> 23], - stencil_op_map[(regs.rb_depthcontrol & 0xE0000000) >> 29], - stencil_op_map[(regs.rb_depthcontrol & 0x1C000000) >> 26]); - } else { - // Backfaces disabled - treat backfaces as frontfaces. - glStencilFunc(compare_func_map[(regs.rb_depthcontrol & 0x00000700) >> 8], - stencil_ref, stencil_read_mask); - glStencilOp(stencil_op_map[(regs.rb_depthcontrol & 0x00003800) >> 11], - stencil_op_map[(regs.rb_depthcontrol & 0x000E0000) >> 17], - stencil_op_map[(regs.rb_depthcontrol & 0x0001C000) >> 14]); - } - - return UpdateStatus::kMismatch; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateIndexBuffer( - IndexBufferInfo* index_buffer_info) { - auto& regs = *register_file_; - if (!index_buffer_info || !index_buffer_info->guest_base) { - // No index buffer or auto draw. - return UpdateStatus::kCompatible; - } - auto& info = *index_buffer_info; - -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - // Min/max index ranges for clamping. This is often [0g,FFFF|FFFFFF]. - // All indices should be clamped to [min,max]. May be a way to do this in GL. - uint32_t min_index = regs[XE_GPU_REG_VGT_MIN_VTX_INDX].u32; - uint32_t max_index = regs[XE_GPU_REG_VGT_MAX_VTX_INDX].u32; - assert_true(min_index == 0); - assert_true(max_index == 0xFFFF || max_index == 0xFFFFFF); - - assert_true(info.endianness == Endian::k8in16 || - info.endianness == Endian::k8in32); - - trace_writer_.WriteMemoryRead(info.guest_base, info.length); - - size_t total_size = - info.count * (info.format == IndexFormat::kInt32 ? sizeof(uint32_t) - : sizeof(uint16_t)); - CircularBuffer::Allocation allocation; - if (!scratch_buffer_.AcquireCached(info.guest_base, total_size, - &allocation)) { - if (info.format == IndexFormat::kInt32) { - auto dest = reinterpret_cast(allocation.host_ptr); - auto src = memory_->TranslatePhysical(info.guest_base); - xe::copy_and_swap_32_aligned(dest, src, info.count); - } else { - auto dest = reinterpret_cast(allocation.host_ptr); - auto src = memory_->TranslatePhysical(info.guest_base); - xe::copy_and_swap_16_aligned(dest, src, info.count); - } - draw_batcher_.set_index_buffer(allocation); - scratch_buffer_.Commit(std::move(allocation)); - } else { - draw_batcher_.set_index_buffer(allocation); - } - - return UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateVertexBuffers() { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - auto& regs = *register_file_; - assert_not_null(active_vertex_shader_); - - for (const auto& vertex_binding : active_vertex_shader_->vertex_bindings()) { - int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + - (vertex_binding.fetch_constant / 3) * 6; - const auto group = reinterpret_cast(®s.values[r]); - const xe_gpu_vertex_fetch_t* fetch = nullptr; - switch (vertex_binding.fetch_constant % 3) { - case 0: - fetch = &group->vertex_fetch_0; - break; - case 1: - fetch = &group->vertex_fetch_1; - break; - case 2: - fetch = &group->vertex_fetch_2; - break; - } - assert_true(fetch->endian == 2); - - size_t valid_range = size_t(fetch->size * 4); - - trace_writer_.WriteMemoryRead(fetch->address << 2, valid_range); - - auto vertex_shader = static_cast(active_vertex_shader_); - CircularBuffer::Allocation allocation; - if (!scratch_buffer_.AcquireCached(fetch->address << 2, valid_range, - &allocation)) { - // Copy and byte swap the entire buffer. - // We could be smart about this to save GPU bandwidth by building a CRC - // as we copy and only if it differs from the previous value committing - // it (and if it matches just discard and reuse). - xe::copy_and_swap_32_aligned( - allocation.host_ptr, - memory_->TranslatePhysical(fetch->address << 2), - valid_range / 4); - - // TODO(benvanik): if we could find a way to avoid this, we could use - // multidraw without flushing. - glVertexArrayVertexBuffer( - vertex_shader->vao(), - static_cast(vertex_binding.binding_index), - scratch_buffer_.handle(), allocation.offset, - vertex_binding.stride_words * 4); - - scratch_buffer_.Commit(std::move(allocation)); - } else { - // TODO(benvanik): if we could find a way to avoid this, we could use - // multidraw without flushing. - glVertexArrayVertexBuffer( - vertex_shader->vao(), - static_cast(vertex_binding.binding_index), - scratch_buffer_.handle(), allocation.offset, - vertex_binding.stride_words * 4); - } - } - - return UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSamplers() { -#if FINE_GRAINED_DRAW_SCOPES - SCOPE_profile_cpu_f("gpu"); -#endif // FINE_GRAINED_DRAW_SCOPES - - bool mismatch = false; - - // VS and PS samplers are shared, but may be used exclusively. - // We walk each and setup lazily. - bool has_setup_sampler[32] = {false}; - - // Vertex texture samplers. - for (auto& texture_binding : active_vertex_shader_->texture_bindings()) { - if (has_setup_sampler[texture_binding.fetch_constant]) { - continue; - } - has_setup_sampler[texture_binding.fetch_constant] = true; - auto status = PopulateSampler(texture_binding); - if (status == UpdateStatus::kError) { - return status; - } else if (status == UpdateStatus::kMismatch) { - mismatch = true; - } - } - - // Pixel shader texture sampler. - for (auto& texture_binding : active_pixel_shader_->texture_bindings()) { - if (has_setup_sampler[texture_binding.fetch_constant]) { - continue; - } - has_setup_sampler[texture_binding.fetch_constant] = true; - auto status = PopulateSampler(texture_binding); - if (status == UpdateStatus::kError) { - return UpdateStatus::kError; - } else if (status == UpdateStatus::kMismatch) { - mismatch = true; - } - } - - return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible; -} - -GL4CommandProcessor::UpdateStatus GL4CommandProcessor::PopulateSampler( - const Shader::TextureBinding& texture_binding) { - auto& regs = *register_file_; - int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + - texture_binding.fetch_constant * 6; - auto group = reinterpret_cast(®s.values[r]); - auto& fetch = group->texture_fetch; - - // Reset slot. - // If we fail, we still draw but with an invalid texture. - draw_batcher_.set_texture_sampler(texture_binding.fetch_constant, 0, 0); - - if (FLAGS_disable_textures) { - return UpdateStatus::kCompatible; - } - - // ? - if (!fetch.type) { - return UpdateStatus::kCompatible; - } - assert_true(fetch.type == 0x2); - - TextureInfo texture_info; - if (!TextureInfo::Prepare(fetch, &texture_info)) { - XELOGE("Unable to parse texture fetcher info"); - return UpdateStatus::kCompatible; // invalid texture used - } - SamplerInfo sampler_info; - if (!SamplerInfo::Prepare(fetch, texture_binding.fetch_instr, - &sampler_info)) { - XELOGE("Unable to parse sampler info"); - return UpdateStatus::kCompatible; // invalid texture used - } - - trace_writer_.WriteMemoryRead(texture_info.guest_address, - texture_info.input_length); - - auto entry_view = texture_cache_.Demand(texture_info, sampler_info); - if (!entry_view) { - // Unable to create/fetch/etc. - XELOGE("Failed to demand texture"); - return UpdateStatus::kCompatible; - } - - // Shaders will use bindless to fetch right from it. - draw_batcher_.set_texture_sampler(texture_binding.fetch_constant, - entry_view->texture_sampler_handle, - fetch.swizzle); - - return UpdateStatus::kCompatible; -} - -bool GL4CommandProcessor::IssueCopy() { - SCOPE_profile_cpu_f("gpu"); - auto& regs = *register_file_; - - // This is used to resolve surfaces, taking them from EDRAM render targets - // to system memory. It can optionally clear color/depth surfaces, too. - // The command buffer has stuff for actually doing this by drawing, however - // we should be able to do it without that much easier. - - uint32_t copy_control = regs[XE_GPU_REG_RB_COPY_CONTROL].u32; - // Render targets 0-3, 4 = depth - uint32_t copy_src_select = copy_control & 0x7; - bool color_clear_enabled = (copy_control >> 8) & 0x1; - bool depth_clear_enabled = (copy_control >> 9) & 0x1; - auto copy_command = static_cast((copy_control >> 20) & 0x3); - - uint32_t copy_dest_info = regs[XE_GPU_REG_RB_COPY_DEST_INFO].u32; - auto copy_dest_endian = static_cast(copy_dest_info & 0x7); - uint32_t copy_dest_array = (copy_dest_info >> 3) & 0x1; - assert_true(copy_dest_array == 0); - uint32_t copy_dest_slice = (copy_dest_info >> 4) & 0x7; - assert_true(copy_dest_slice == 0); - auto copy_dest_format = - static_cast((copy_dest_info >> 7) & 0x3F); - uint32_t copy_dest_number = (copy_dest_info >> 13) & 0x7; - // assert_true(copy_dest_number == 0); // ? - uint32_t copy_dest_bias = (copy_dest_info >> 16) & 0x3F; - // assert_true(copy_dest_bias == 0); - uint32_t copy_dest_swap = (copy_dest_info >> 25) & 0x1; - - uint32_t copy_dest_base = regs[XE_GPU_REG_RB_COPY_DEST_BASE].u32; - uint32_t copy_dest_pitch = regs[XE_GPU_REG_RB_COPY_DEST_PITCH].u32; - uint32_t copy_dest_height = (copy_dest_pitch >> 16) & 0x3FFF; - copy_dest_pitch &= 0x3FFF; - - // None of this is supported yet: - uint32_t copy_surface_slice = regs[XE_GPU_REG_RB_COPY_SURFACE_SLICE].u32; - assert_true(copy_surface_slice == 0); - uint32_t copy_func = regs[XE_GPU_REG_RB_COPY_FUNC].u32; - assert_true(copy_func == 0); - uint32_t copy_ref = regs[XE_GPU_REG_RB_COPY_REF].u32; - assert_true(copy_ref == 0); - uint32_t copy_mask = regs[XE_GPU_REG_RB_COPY_MASK].u32; - assert_true(copy_mask == 0); - - // RB_SURFACE_INFO - // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html - uint32_t surface_info = regs[XE_GPU_REG_RB_SURFACE_INFO].u32; - uint32_t surface_pitch = surface_info & 0x3FFF; - auto surface_msaa = static_cast((surface_info >> 16) & 0x3); - - // Depending on the source, pick the buffer we'll be sourcing. - // We then query for a cached framebuffer setup with that buffer active. - TextureFormat src_format = TextureFormat::kUnknown; - GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget}; - GLuint depth_target = kAnyTarget; - if (copy_src_select <= 3 || color_clear_enabled) { - // Source from a color target. - uint32_t color_info[4] = { - regs[XE_GPU_REG_RB_COLOR_INFO].u32, - regs[XE_GPU_REG_RB_COLOR1_INFO].u32, - regs[XE_GPU_REG_RB_COLOR2_INFO].u32, - regs[XE_GPU_REG_RB_COLOR3_INFO].u32, - }; - uint32_t color_base = color_info[copy_src_select] & 0xFFF; - auto color_format = static_cast( - (color_info[copy_src_select] >> 16) & 0xF); - color_targets[copy_src_select] = GetColorRenderTarget( - surface_pitch, surface_msaa, color_base, color_format); - - if (copy_src_select <= 3) { - src_format = ColorRenderTargetToTextureFormat(color_format); - } - } - - // Grab the depth/stencil if we're sourcing from it or clear is enabled. - if (copy_src_select > 3 || depth_clear_enabled) { - uint32_t depth_info = regs[XE_GPU_REG_RB_DEPTH_INFO].u32; - uint32_t depth_base = depth_info & 0xFFF; - auto depth_format = - static_cast((depth_info >> 16) & 0x1); - depth_target = GetDepthRenderTarget(surface_pitch, surface_msaa, depth_base, - depth_format); - - if (copy_src_select > 3) { - src_format = DepthRenderTargetToTextureFormat(depth_format); - } - } - - auto source_framebuffer = GetFramebuffer(color_targets, depth_target); - if (!source_framebuffer) { - // If we get here we are likely missing some state checks. - assert_always("No framebuffer for copy source? no-op copy?"); - XELOGE("No framebuffer for copy source"); - return false; - } - - active_framebuffer_ = source_framebuffer; - - GLenum read_format; - GLenum read_type; - size_t read_size = 0; - switch (copy_dest_format) { - case ColorFormat::k_1_5_5_5: - read_format = GL_RGB5_A1; - read_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - read_size = 16; - break; - case ColorFormat::k_2_10_10_10: - read_format = GL_RGB10_A2; - read_type = GL_UNSIGNED_INT_10_10_10_2; - read_size = 32; - break; - case ColorFormat::k_4_4_4_4: - read_format = GL_RGBA4; - read_type = GL_UNSIGNED_SHORT_4_4_4_4; - read_size = 16; - break; - case ColorFormat::k_5_6_5: - read_format = GL_RGB565; - read_type = GL_UNSIGNED_SHORT_5_6_5; - read_size = 16; - break; - case ColorFormat::k_8: - read_format = GL_R8; - read_type = GL_UNSIGNED_BYTE; - read_size = 8; - break; - case ColorFormat::k_8_8: - read_format = GL_RG8; - read_type = GL_UNSIGNED_BYTE; - read_size = 16; - break; - case ColorFormat::k_8_8_8_8: - read_format = copy_dest_swap ? GL_BGRA : GL_RGBA; - read_type = GL_UNSIGNED_BYTE; - read_size = 32; - break; - case ColorFormat::k_16: - read_format = GL_R16; - read_type = GL_UNSIGNED_SHORT; - read_size = 16; - break; - case ColorFormat::k_16_FLOAT: - read_format = GL_R16F; - read_type = GL_HALF_FLOAT; - read_size = 16; - break; - case ColorFormat::k_16_16: - read_format = GL_RG16; - read_type = GL_UNSIGNED_SHORT; - read_size = 32; - break; - case ColorFormat::k_16_16_FLOAT: - read_format = GL_RG16F; - read_type = GL_HALF_FLOAT; - read_size = 32; - break; - case ColorFormat::k_16_16_16_16: - read_format = GL_RGBA16; - read_type = GL_UNSIGNED_SHORT; - read_size = 64; - break; - case ColorFormat::k_16_16_16_16_FLOAT: - read_format = GL_RGBA16F; - read_type = GL_HALF_FLOAT; - read_size = 64; - break; - case ColorFormat::k_32_FLOAT: - read_format = GL_R32F; - read_type = GL_FLOAT; - read_size = 32; - break; - case ColorFormat::k_32_32_FLOAT: - read_format = GL_RG32F; - read_type = GL_FLOAT; - read_size = 64; - break; - case ColorFormat::k_32_32_32_32_FLOAT: - read_format = GL_RGBA32F; - read_type = GL_FLOAT; - read_size = 128; - break; - case ColorFormat::k_10_11_11: - case ColorFormat::k_11_11_10: - read_format = GL_R11F_G11F_B10F; - read_type = GL_UNSIGNED_INT_10F_11F_11F_REV; - read_size = 32; - break; - default: - assert_unhandled_case(copy_dest_format); - return false; - } - - // TODO(benvanik): swap channel ordering on copy_dest_swap - // Can we use GL swizzles for this? - - // Swap byte order during read. - // TODO(benvanik): handle other endian modes. - switch (copy_dest_endian) { - case Endian128::kUnspecified: - glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); - break; - case Endian128::k8in32: - glPixelStorei(GL_PACK_SWAP_BYTES, GL_TRUE); - break; - default: - // assert_unhandled_case(copy_dest_endian); - glPixelStorei(GL_PACK_SWAP_BYTES, GL_TRUE); - break; - } - - // TODO(benvanik): tweak alignments/strides. - // glPixelStorei(GL_PACK_ALIGNMENT, 1); - // glPixelStorei(GL_PACK_ROW_LENGTH, 0); - // glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); - - // TODO(benvanik): any way to scissor this? a200 has: - // REG_A2XX_RB_COPY_DEST_OFFSET = A2XX_RB_COPY_DEST_OFFSET_X(tile->xoff) | - // A2XX_RB_COPY_DEST_OFFSET_Y(tile->yoff); - // but I can't seem to find something similar. - uint32_t dest_logical_width = copy_dest_pitch; - uint32_t dest_logical_height = copy_dest_height; - uint32_t dest_block_width = xe::round_up(dest_logical_width, 32); - uint32_t dest_block_height = /*xe::round_up(*/ dest_logical_height /*, 32)*/; - - uint32_t window_offset = regs[XE_GPU_REG_PA_SC_WINDOW_OFFSET].u32; - int16_t window_offset_x = window_offset & 0x7FFF; - int16_t window_offset_y = (window_offset >> 16) & 0x7FFF; - if (window_offset_x & 0x4000) { - window_offset_x |= 0x8000; - } - if (window_offset_y & 0x4000) { - window_offset_y |= 0x8000; - } - - // HACK: vertices to use are always in vf0. - int copy_vertex_fetch_slot = 0; - int r = - XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + (copy_vertex_fetch_slot / 3) * 6; - const auto group = reinterpret_cast(®s.values[r]); - const xe_gpu_vertex_fetch_t* fetch = nullptr; - switch (copy_vertex_fetch_slot % 3) { - case 0: - fetch = &group->vertex_fetch_0; - break; - case 1: - fetch = &group->vertex_fetch_1; - break; - case 2: - fetch = &group->vertex_fetch_2; - break; - } - assert_true(fetch->type == 3); - assert_true(fetch->endian == 2); - assert_true(fetch->size == 6); - const uint8_t* vertex_addr = memory_->TranslatePhysical(fetch->address << 2); - trace_writer_.WriteMemoryRead(fetch->address << 2, fetch->size * 4); - int32_t dest_min_x = int32_t((std::min( - std::min( - GpuSwap(xe::load(vertex_addr + 0), Endian(fetch->endian)), - GpuSwap(xe::load(vertex_addr + 8), Endian(fetch->endian))), - GpuSwap(xe::load(vertex_addr + 16), Endian(fetch->endian))))); - int32_t dest_max_x = int32_t((std::max( - std::max( - GpuSwap(xe::load(vertex_addr + 0), Endian(fetch->endian)), - GpuSwap(xe::load(vertex_addr + 8), Endian(fetch->endian))), - GpuSwap(xe::load(vertex_addr + 16), Endian(fetch->endian))))); - int32_t dest_min_y = int32_t((std::min( - std::min( - GpuSwap(xe::load(vertex_addr + 4), Endian(fetch->endian)), - GpuSwap(xe::load(vertex_addr + 12), Endian(fetch->endian))), - GpuSwap(xe::load(vertex_addr + 20), Endian(fetch->endian))))); - int32_t dest_max_y = int32_t((std::max( - std::max( - GpuSwap(xe::load(vertex_addr + 4), Endian(fetch->endian)), - GpuSwap(xe::load(vertex_addr + 12), Endian(fetch->endian))), - GpuSwap(xe::load(vertex_addr + 20), Endian(fetch->endian))))); - Rect2D dest_rect(dest_min_x, dest_min_y, dest_max_x - dest_min_x, - dest_max_y - dest_min_y); - Rect2D src_rect(0, 0, dest_rect.width, dest_rect.height); - - // The dest base address passed in has already been offset by the window - // offset, so to ensure texture lookup works we need to offset it. - // TODO(benvanik): allow texture cache to lookup partial textures. - // TODO(benvanik): change based on format. - int32_t dest_offset = window_offset_y * copy_dest_pitch * int(read_size / 8); - dest_offset += window_offset_x * 32 * int(read_size / 8); - copy_dest_base += dest_offset; - - // Destination pointer in guest memory. - // We have GL throw bytes directly into it. - // TODO(benvanik): copy to staging texture then PBO back? - void* ptr = memory_->TranslatePhysical(copy_dest_base); - size_t size = copy_dest_pitch * copy_dest_height * (read_size / 8); - - auto blitter = static_cast(context_.get())->blitter(); - - // Make active so glReadPixels reads from us. - switch (copy_command) { - case CopyCommand::kRaw: { - // This performs a byte-for-byte copy of the textures from src to dest - // with no conversion. Byte swapping may still occur. - if (copy_src_select <= 3) { - // Source from a bound render target. - // TODO(benvanik): RAW copy. - last_framebuffer_texture_ = texture_cache_.CopyTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, - ColorFormatToTextureFormat(copy_dest_format), - copy_dest_swap ? true : false, color_targets[copy_src_select], - src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, read_format, - // read_type, ptr); - } - } else { - // Source from the bound depth/stencil target. - // TODO(benvanik): RAW copy. - texture_cache_.CopyTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, src_format, - copy_dest_swap ? true : false, depth_target, src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, - // GL_DEPTH_STENCIL, read_type, ptr); - } - } - break; - } - case CopyCommand::kConvert: { - if (copy_src_select <= 3) { - // Source from a bound render target. - // Either copy the readbuffer into an existing texture or create a new - // one in the cache so we can service future upload requests. - last_framebuffer_texture_ = texture_cache_.ConvertTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, - ColorFormatToTextureFormat(copy_dest_format), - copy_dest_swap ? true : false, color_targets[copy_src_select], - src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, read_format, - // read_type, ptr); - } - } else { - // Source from the bound depth/stencil target. - texture_cache_.ConvertTexture( - blitter, copy_dest_base, dest_logical_width, dest_logical_height, - dest_block_width, dest_block_height, src_format, - copy_dest_swap ? true : false, depth_target, src_rect, dest_rect); - if (!FLAGS_disable_framebuffer_readback) { - // std::memset(ptr, 0xDE, - // copy_dest_pitch * copy_dest_height * (read_size / 8)); - // glReadPixels(0, 0, copy_dest_pitch, copy_dest_height, - // GL_DEPTH_STENCIL, read_type, ptr); - } - } - break; - } - case CopyCommand::kConstantOne: - case CopyCommand::kNull: - default: - // assert_unhandled_case(copy_command); - return false; - } - - // Perform any requested clears. - uint32_t copy_depth_clear = regs[XE_GPU_REG_RB_DEPTH_CLEAR].u32; - uint32_t copy_color_clear = regs[XE_GPU_REG_RB_COLOR_CLEAR].u32; - uint32_t copy_color_clear_low = regs[XE_GPU_REG_RB_COLOR_CLEAR_LOW].u32; - assert_true(copy_color_clear == copy_color_clear_low); - - if (color_clear_enabled) { - // Clear the render target we selected for copy. - assert_true(copy_src_select < 3); - // TODO(benvanik): verify color order. - float color[] = {(copy_color_clear & 0xFF) / 255.0f, - ((copy_color_clear >> 8) & 0xFF) / 255.0f, - ((copy_color_clear >> 16) & 0xFF) / 255.0f, - ((copy_color_clear >> 24) & 0xFF) / 255.0f}; - // TODO(benvanik): remove query. - GLboolean old_color_mask[4]; - glGetBooleani_v(GL_COLOR_WRITEMASK, copy_src_select, old_color_mask); - glColorMaski(copy_src_select, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearNamedFramebufferfv(source_framebuffer->framebuffer, GL_COLOR, - copy_src_select, color); - glColorMaski(copy_src_select, old_color_mask[0], old_color_mask[1], - old_color_mask[2], old_color_mask[3]); - } - - if (depth_clear_enabled && depth_target != kAnyTarget) { - // Clear the current depth buffer. - // TODO(benvanik): verify format. - GLfloat depth = {(copy_depth_clear & 0xFFFFFF00) / - static_cast(0xFFFFFF00)}; - GLint stencil = copy_depth_clear & 0xFF; - GLint old_draw_framebuffer; - GLboolean old_depth_mask; - GLint old_stencil_mask; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_framebuffer); - glGetBooleanv(GL_DEPTH_WRITEMASK, &old_depth_mask); - glGetIntegerv(GL_STENCIL_WRITEMASK, &old_stencil_mask); - glDepthMask(GL_TRUE); - glStencilMask(0xFF); - // HACK: this should work, but throws INVALID_ENUM on nvidia drivers. - // GLEW signature differs from OpenGL docs? - // glClearNamedFramebufferfi(source_framebuffer->framebuffer, - // GL_DEPTH_STENCIL, depth, stencil); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, source_framebuffer->framebuffer); - glClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_framebuffer); - glDepthMask(old_depth_mask); - glStencilMask(old_stencil_mask); - } - - return true; -} - -GLuint GL4CommandProcessor::GetColorRenderTarget( - uint32_t pitch, MsaaSamples samples, uint32_t base, - ColorRenderTargetFormat format) { - // Because we don't know the height of anything, we allocate at full res. - // At 2560x2560, it's impossible for EDRAM to fit anymore. - uint32_t width = 2560; - uint32_t height = 2560; - - // NOTE: we strip gamma formats down to normal ones. - if (format == ColorRenderTargetFormat::k_8_8_8_8_GAMMA) { - format = ColorRenderTargetFormat::k_8_8_8_8; - } - - GLenum internal_format; - switch (format) { - case ColorRenderTargetFormat::k_8_8_8_8: - case ColorRenderTargetFormat::k_8_8_8_8_GAMMA: - internal_format = GL_RGBA8; - break; - case ColorRenderTargetFormat::k_2_10_10_10: - case ColorRenderTargetFormat::k_2_10_10_10_unknown: - internal_format = GL_RGB10_A2UI; - break; - case ColorRenderTargetFormat::k_2_10_10_10_FLOAT: - case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: - internal_format = GL_RGB10_A2; - break; - case ColorRenderTargetFormat::k_16_16: - internal_format = GL_RG16; - break; - case ColorRenderTargetFormat::k_16_16_FLOAT: - internal_format = GL_RG16F; - break; - case ColorRenderTargetFormat::k_16_16_16_16: - internal_format = GL_RGBA16; - break; - case ColorRenderTargetFormat::k_16_16_16_16_FLOAT: - internal_format = GL_RGBA16F; - break; - case ColorRenderTargetFormat::k_32_FLOAT: - internal_format = GL_R32F; - break; - case ColorRenderTargetFormat::k_32_32_FLOAT: - internal_format = GL_RG32F; - break; - default: - assert_unhandled_case(format); - return 0; - } - - for (auto it = cached_color_render_targets_.begin(); - it != cached_color_render_targets_.end(); ++it) { - if (it->base == base && it->width == width && it->height == height && - it->internal_format == internal_format) { - return it->texture; - } - } - cached_color_render_targets_.push_back(CachedColorRenderTarget()); - auto cached = &cached_color_render_targets_.back(); - cached->base = base; - cached->width = width; - cached->height = height; - cached->format = format; - cached->internal_format = internal_format; - - glCreateTextures(GL_TEXTURE_2D, 1, &cached->texture); - glTextureStorage2D(cached->texture, 1, internal_format, width, height); - - return cached->texture; -} - -GLuint GL4CommandProcessor::GetDepthRenderTarget( - uint32_t pitch, MsaaSamples samples, uint32_t base, - DepthRenderTargetFormat format) { - uint32_t width = 2560; - uint32_t height = 2560; - - GLenum internal_format; - switch (format) { - case DepthRenderTargetFormat::kD24S8: - internal_format = GL_DEPTH24_STENCIL8; - break; - case DepthRenderTargetFormat::kD24FS8: - // TODO(benvanik): not supported in GL? - internal_format = GL_DEPTH24_STENCIL8; - break; - default: - assert_unhandled_case(format); - return 0; - } - - for (auto it = cached_depth_render_targets_.begin(); - it != cached_depth_render_targets_.end(); ++it) { - if (it->base == base && it->width == width && it->height == height && - it->format == format) { - return it->texture; - } - } - cached_depth_render_targets_.push_back(CachedDepthRenderTarget()); - auto cached = &cached_depth_render_targets_.back(); - cached->base = base; - cached->width = width; - cached->height = height; - cached->format = format; - cached->internal_format = internal_format; - - glCreateTextures(GL_TEXTURE_2D, 1, &cached->texture); - glTextureStorage2D(cached->texture, 1, internal_format, width, height); - - return cached->texture; -} - -GL4CommandProcessor::CachedFramebuffer* GL4CommandProcessor::GetFramebuffer( - GLuint color_targets[4], GLuint depth_target) { - for (auto it = cached_framebuffers_.begin(); it != cached_framebuffers_.end(); - ++it) { - if ((depth_target == kAnyTarget || it->depth_target == depth_target) && - (color_targets[0] == kAnyTarget || - it->color_targets[0] == color_targets[0]) && - (color_targets[1] == kAnyTarget || - it->color_targets[1] == color_targets[1]) && - (color_targets[2] == kAnyTarget || - it->color_targets[2] == color_targets[2]) && - (color_targets[3] == kAnyTarget || - it->color_targets[3] == color_targets[3])) { - return &*it; - } - } - - GLuint real_color_targets[4]; - bool any_set = false; - for (int i = 0; i < 4; ++i) { - if (color_targets[i] == kAnyTarget) { - real_color_targets[i] = 0; - } else { - any_set = true; - real_color_targets[i] = color_targets[i]; - } - } - GLuint real_depth_target; - if (depth_target == kAnyTarget) { - real_depth_target = 0; - } else { - any_set = true; - real_depth_target = depth_target; - } - if (!any_set) { - // No framebuffer required. - return nullptr; - } - - cached_framebuffers_.push_back(CachedFramebuffer()); - auto cached = &cached_framebuffers_.back(); - glCreateFramebuffers(1, &cached->framebuffer); - for (int i = 0; i < 4; ++i) { - cached->color_targets[i] = real_color_targets[i]; - glNamedFramebufferTexture(cached->framebuffer, GL_COLOR_ATTACHMENT0 + i, - real_color_targets[i], 0); - } - cached->depth_target = real_depth_target; - glNamedFramebufferTexture(cached->framebuffer, GL_DEPTH_STENCIL_ATTACHMENT, - real_depth_target, 0); - - return cached; -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/gl4_command_processor.h b/src/xenia/gpu/gl4/gl4_command_processor.h deleted file mode 100644 index e3f45c9eb..000000000 --- a/src/xenia/gpu/gl4/gl4_command_processor.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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 cached_framebuffers_; - std::vector cached_color_render_targets_; - std::vector cached_depth_render_targets_; - std::vector> all_pipelines_; - std::unordered_map 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_ diff --git a/src/xenia/gpu/gl4/gl4_gpu_flags.cc b/src/xenia/gpu/gl4/gl4_gpu_flags.cc deleted file mode 100644 index 3844bfc95..000000000 --- a/src/xenia/gpu/gl4/gl4_gpu_flags.cc +++ /dev/null @@ -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."); diff --git a/src/xenia/gpu/gl4/gl4_gpu_flags.h b/src/xenia/gpu/gl4/gl4_gpu_flags.h deleted file mode 100644 index 9f68287f9..000000000 --- a/src/xenia/gpu/gl4/gl4_gpu_flags.h +++ /dev/null @@ -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 - -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_ diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc deleted file mode 100644 index efca2fa08..000000000 --- a/src/xenia/gpu/gl4/gl4_graphics_system.cc +++ /dev/null @@ -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 -#include - -#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(target_window->context()); - - return X_STATUS_SUCCESS; -} - -void GL4GraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); } - -std::unique_ptr GL4GraphicsSystem::CreateCommandProcessor() { - return std::unique_ptr( - 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 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(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 diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h deleted file mode 100644 index 9644db075..000000000 --- a/src/xenia/gpu/gl4/gl4_graphics_system.h +++ /dev/null @@ -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 - -#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 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_ diff --git a/src/xenia/gpu/gl4/gl4_shader.cc b/src/xenia/gpu/gl4/gl4_shader.cc deleted file mode 100644 index 7f34b1957..000000000 --- a/src/xenia/gpu/gl4/gl4_shader.cc +++ /dev/null @@ -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 GL4Shader::GetBinary(GLenum* binary_format) { - std::vector 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& 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(binary.data()); - while (true) { - auto p = reinterpret_cast(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 diff --git a/src/xenia/gpu/gl4/gl4_shader.h b/src/xenia/gpu/gl4/gl4_shader.h deleted file mode 100644 index 1f884ebf2..000000000 --- a/src/xenia/gpu/gl4/gl4_shader.h +++ /dev/null @@ -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 - -#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 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& binary); - - GLuint program_ = 0; - GLuint shader_ = 0; - GLuint vao_ = 0; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_GL4_SHADER_H_ diff --git a/src/xenia/gpu/gl4/gl4_shader_cache.cc b/src/xenia/gpu/gl4/gl4_shader_cache.cc deleted file mode 100644 index 714de3e1d..000000000 --- a/src/xenia/gpu/gl4/gl4_shader_cache.cc +++ /dev/null @@ -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 - -#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(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 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(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(map->data()); - // TODO: Compare versions - if (cached_shader->magic != xe::byte_swap('XSHD')) { - return nullptr; - } - - auto shader = - std::make_unique(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 diff --git a/src/xenia/gpu/gl4/gl4_shader_cache.h b/src/xenia/gpu/gl4/gl4_shader_cache.h deleted file mode 100644 index 9c5c77cb2..000000000 --- a/src/xenia/gpu/gl4/gl4_shader_cache.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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> all_shaders_; - std::unordered_map shader_map_; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_SHADER_CACHE_H_ diff --git a/src/xenia/gpu/gl4/gl4_trace_viewer_main.cc b/src/xenia/gpu/gl4/gl4_trace_viewer_main.cc deleted file mode 100644 index c7d24001c..000000000 --- a/src/xenia/gpu/gl4/gl4_trace_viewer_main.cc +++ /dev/null @@ -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 CreateGraphicsSystem() override { - return std::unique_ptr(new GL4GraphicsSystem()); - } - - uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples, - uint32_t base, - ColorRenderTargetFormat format) override { - auto command_processor = static_cast( - 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( - 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( - 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(texture->handle); - } - - size_t QueryVSOutputSize() override { - auto command_processor = static_cast( - 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( - 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( - 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& 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); diff --git a/src/xenia/gpu/gl4/premake5.lua b/src/xenia/gpu/gl4/premake5.lua deleted file mode 100644 index 041ef8b2d..000000000 --- a/src/xenia/gpu/gl4/premake5.lua +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/src/xenia/gpu/gl4/texture_cache.cc b/src/xenia/gpu/gl4/texture_cache.cc deleted file mode 100644 index affd012bd..000000000 --- a/src/xenia/gpu/gl4/texture_cache.cc +++ /dev/null @@ -1,1101 +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/texture_cache.h" - -#include -#include - -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/memory.h" -#include "xenia/base/profiling.h" -#include "xenia/gpu/gpu_flags.h" - -namespace xe { -namespace gpu { -namespace gl4 { - -struct TextureConfig { - TextureFormat texture_format; - GLenum internal_format; - GLenum format; - GLenum type; -}; - -// https://code.google.com/p/glsnewton/source/browse/trunk/Source/uDDSLoader.pas?r=62 -// http://dench.flatlib.jp/opengl/textures -// http://fossies.org/linux/WebKit/Source/ThirdParty/ANGLE/src/libGLESv2/formatutils.cpp -static const TextureConfig texture_configs[64] = { - {TextureFormat::k_1_REVERSE, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, - {TextureFormat::k_1_5_5_5, GL_RGB5_A1, GL_RGBA, - GL_UNSIGNED_SHORT_1_5_5_5_REV}, - {TextureFormat::k_5_6_5, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, - {TextureFormat::k_6_5_5, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, - {TextureFormat::k_2_10_10_10, GL_RGB10_A2, GL_RGBA, - GL_UNSIGNED_INT_2_10_10_10_REV}, - {TextureFormat::k_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_B, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_8_8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, - {TextureFormat::k_Cr_Y1_Cb_Y0, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_Y1_Cr_Y0_Cb, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8_A, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_4_4_4_4, GL_RGBA4, GL_RGBA, - GL_UNSIGNED_SHORT_4_4_4_4_REV}, - {TextureFormat::k_10_11_11, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? - {TextureFormat::k_11_11_10, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, // ? - {TextureFormat::k_DXT1, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT2_3, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT4_5, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_24_8, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, - GL_UNSIGNED_INT_24_8}, - {TextureFormat::k_24_8_FLOAT, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, - GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, - {TextureFormat::k_16, GL_R16, GL_RED, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_16_16, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_EXPAND, GL_R16, GL_RED, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_EXPAND, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_16_16_16_EXPAND, GL_RGBA16, GL_RGBA, - GL_UNSIGNED_SHORT}, - {TextureFormat::k_16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT}, - {TextureFormat::k_16_16_FLOAT, GL_RG16F, GL_RG, GL_HALF_FLOAT}, - {TextureFormat::k_16_16_16_16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, - {TextureFormat::k_32, GL_R32I, GL_RED, GL_UNSIGNED_INT}, - {TextureFormat::k_32_32, GL_RG32I, GL_RG, GL_UNSIGNED_INT}, - {TextureFormat::k_32_32_32_32, GL_RGBA32I, GL_RGBA, GL_UNSIGNED_INT}, - {TextureFormat::k_32_FLOAT, GL_R32F, GL_RED, GL_FLOAT}, - {TextureFormat::k_32_32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT}, - {TextureFormat::k_32_32_32_32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT}, - {TextureFormat::k_32_AS_8, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_8, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_MPEG, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_32_AS_8_8_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_16_16_MPEG_INTERLACED, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::k_DXN, GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, - GL_INVALID_ENUM}, - {TextureFormat::k_8_8_8_8_AS_16_16_16_16, GL_RGBA8, GL_RGBA, - GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT1_AS_16_16_16_16, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, - GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT2_3_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT4_5_AS_16_16_16_16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_2_10_10_10_AS_16_16_16_16, GL_RGB10_A2, GL_RGBA, - GL_UNSIGNED_INT_2_10_10_10_REV}, - {TextureFormat::k_10_11_11_AS_16_16_16_16, GL_R11F_G11F_B10F, GL_RGB, - GL_UNSIGNED_INT_10F_11F_11F_REV}, - {TextureFormat::k_11_11_10_AS_16_16_16_16, GL_R11F_G11F_B10F, - GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_32_32_32_FLOAT, GL_RGB32F, GL_RGB, GL_FLOAT}, - {TextureFormat::k_DXT3A, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_DXT5A, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE}, - {TextureFormat::k_CTX1, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM}, - {TextureFormat::k_DXT3A_AS_1_1_1_1, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, - {TextureFormat::kUnknown, GL_INVALID_ENUM, GL_INVALID_ENUM, - GL_INVALID_ENUM}, -}; - -TextureCache::TextureCache() : memory_(nullptr), scratch_buffer_(nullptr) { - invalidated_textures_sets_[0].reserve(64); - invalidated_textures_sets_[1].reserve(64); - invalidated_textures_ = &invalidated_textures_sets_[0]; -} - -TextureCache::~TextureCache() { Shutdown(); } - -bool TextureCache::Initialize(Memory* memory, CircularBuffer* scratch_buffer) { - memory_ = memory; - scratch_buffer_ = scratch_buffer; - return true; -} - -void TextureCache::Shutdown() { Clear(); } - -void TextureCache::Scavenge() { - invalidated_textures_mutex_.lock(); - std::vector& invalidated_textures = *invalidated_textures_; - if (invalidated_textures_ == &invalidated_textures_sets_[0]) { - invalidated_textures_ = &invalidated_textures_sets_[1]; - } else { - invalidated_textures_ = &invalidated_textures_sets_[0]; - } - invalidated_textures_mutex_.unlock(); - if (invalidated_textures.empty()) { - return; - } - - for (auto& entry : invalidated_textures) { - EvictTexture(entry); - } - invalidated_textures.clear(); -} - -void TextureCache::Clear() { - EvictAllTextures(); - - // Samplers must go last, as textures depend on them. - while (sampler_entries_.size()) { - auto entry = sampler_entries_.begin()->second; - EvictSampler(entry); - } -} - -void TextureCache::EvictAllTextures() { - // Kill all textures - some may be in the eviction list, but that's fine - // as we will clear that below. - while (!texture_entries_.empty()) { - auto entry = texture_entries_.begin()->second; - EvictTexture(entry); - } - - { - std::lock_guard lock(invalidated_textures_mutex_); - invalidated_textures_sets_[0].clear(); - invalidated_textures_sets_[1].clear(); - } - - // Kill all readbuffer textures. - while (!read_buffer_textures_.empty()) { - auto it = --read_buffer_textures_.end(); - auto entry = *it; - glDeleteTextures(1, &entry->handle); - delete entry; - read_buffer_textures_.erase(it); - } -} - -TextureCache::TextureEntryView* TextureCache::Demand( - const TextureInfo& texture_info, const SamplerInfo& sampler_info) { - uint64_t texture_hash = texture_info.hash(); - auto texture_entry = LookupOrInsertTexture(texture_info, texture_hash); - if (!texture_entry) { - XELOGE("Failed to setup texture"); - return nullptr; - } - - // We likely have the sampler in the texture view listing, so scan for it. - uint64_t sampler_hash = sampler_info.hash(); - for (auto& it : texture_entry->views) { - if (it->sampler_hash == sampler_hash) { - // Found. - return it.get(); - } - } - - // No existing view found - build it. - auto sampler_entry = LookupOrInsertSampler(sampler_info, sampler_hash); - if (!sampler_entry) { - XELOGE("Failed to setup texture sampler"); - return nullptr; - } - - auto view = std::make_unique(); - view->texture = texture_entry; - view->sampler = sampler_entry; - view->sampler_hash = sampler_hash; - view->texture_sampler_handle = 0; - - // Get the uvec2 handle to the texture/sampler pair and make it resident. - // The handle can be passed directly to the shader. - view->texture_sampler_handle = glGetTextureSamplerHandleARB( - texture_entry->handle, sampler_entry->handle); - if (!view->texture_sampler_handle) { - assert_always("Unable to get texture handle?"); - return nullptr; - } - glMakeTextureHandleResidentARB(view->texture_sampler_handle); - - // Entry takes ownership. - auto view_ptr = view.get(); - texture_entry->views.push_back(std::move(view)); - return view_ptr; -} - -TextureCache::SamplerEntry* TextureCache::LookupOrInsertSampler( - const SamplerInfo& sampler_info, uint64_t opt_hash) { - const uint64_t hash = opt_hash ? opt_hash : sampler_info.hash(); - for (auto it = sampler_entries_.find(hash); it != sampler_entries_.end(); - ++it) { - if (it->second->sampler_info == sampler_info) { - // Found in cache! - return it->second; - } - } - - // Not found, create. - auto entry = std::make_unique(); - entry->sampler_info = sampler_info; - glCreateSamplers(1, &entry->handle); - - // TODO(benvanik): border color from texture fetch. - GLfloat border_color[4] = {0.0f}; - glSamplerParameterfv(entry->handle, GL_TEXTURE_BORDER_COLOR, border_color); - - // TODO(benvanik): setup LODs for mipmapping. - glSamplerParameterf(entry->handle, GL_TEXTURE_LOD_BIAS, 0.0f); - glSamplerParameterf(entry->handle, GL_TEXTURE_MIN_LOD, 0.0f); - glSamplerParameterf(entry->handle, GL_TEXTURE_MAX_LOD, 0.0f); - - // Texture wrapping modes. - // TODO(benvanik): not sure if the middle ones are correct. - static const GLenum wrap_map[] = { - GL_REPEAT, // - GL_MIRRORED_REPEAT, // - GL_CLAMP_TO_EDGE, // - GL_MIRROR_CLAMP_TO_EDGE, // - GL_CLAMP_TO_BORDER, // ? - GL_MIRROR_CLAMP_TO_BORDER_EXT, // ? - GL_CLAMP_TO_BORDER, // - GL_MIRROR_CLAMP_TO_BORDER_EXT, // - }; - glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_S, - wrap_map[static_cast(sampler_info.clamp_u)]); - glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_T, - wrap_map[static_cast(sampler_info.clamp_v)]); - glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_R, - wrap_map[static_cast(sampler_info.clamp_w)]); - - // Texture level filtering. - GLenum min_filter; - switch (sampler_info.min_filter) { - case TextureFilter::kPoint: - switch (sampler_info.mip_filter) { - case TextureFilter::kBaseMap: - min_filter = GL_NEAREST; - break; - case TextureFilter::kPoint: - // min_filter = GL_NEAREST_MIPMAP_NEAREST; - min_filter = GL_NEAREST; - break; - case TextureFilter::kLinear: - // min_filter = GL_NEAREST_MIPMAP_LINEAR; - min_filter = GL_NEAREST; - break; - default: - assert_unhandled_case(sampler_info.mip_filter); - return nullptr; - } - break; - case TextureFilter::kLinear: - switch (sampler_info.mip_filter) { - case TextureFilter::kBaseMap: - min_filter = GL_LINEAR; - break; - case TextureFilter::kPoint: - // min_filter = GL_LINEAR_MIPMAP_NEAREST; - min_filter = GL_LINEAR; - break; - case TextureFilter::kLinear: - // min_filter = GL_LINEAR_MIPMAP_LINEAR; - min_filter = GL_LINEAR; - break; - default: - assert_unhandled_case(sampler_info.mip_filter); - return nullptr; - } - break; - default: - assert_unhandled_case(sampler_info.min_filter); - return nullptr; - } - GLenum mag_filter; - switch (sampler_info.mag_filter) { - case TextureFilter::kPoint: - mag_filter = GL_NEAREST; - break; - case TextureFilter::kLinear: - mag_filter = GL_LINEAR; - break; - default: - assert_unhandled_case(mag_filter); - return nullptr; - } - glSamplerParameteri(entry->handle, GL_TEXTURE_MIN_FILTER, min_filter); - glSamplerParameteri(entry->handle, GL_TEXTURE_MAG_FILTER, mag_filter); - - GLfloat aniso; - switch (sampler_info.aniso_filter) { - case AnisoFilter::kDisabled: - aniso = 0.0f; - break; - case AnisoFilter::kMax_1_1: - aniso = 1.0f; - break; - case AnisoFilter::kMax_2_1: - aniso = 2.0f; - break; - case AnisoFilter::kMax_4_1: - aniso = 4.0f; - break; - case AnisoFilter::kMax_8_1: - aniso = 8.0f; - break; - case AnisoFilter::kMax_16_1: - aniso = 16.0f; - break; - default: - assert_unhandled_case(aniso); - return nullptr; - } - - if (aniso) { - glSamplerParameterf(entry->handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso); - } - - // Add to map - map takes ownership. - auto entry_ptr = entry.get(); - sampler_entries_.insert({hash, entry.release()}); - return entry_ptr; -} - -void TextureCache::EvictSampler(SamplerEntry* entry) { - glDeleteSamplers(1, &entry->handle); - - for (auto it = sampler_entries_.find(entry->sampler_info.hash()); - it != sampler_entries_.end(); ++it) { - if (it->second == entry) { - sampler_entries_.erase(it); - break; - } - } - - delete entry; -} - -TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture( - const TextureInfo& texture_info, uint64_t opt_hash) { - const uint64_t hash = opt_hash ? opt_hash : texture_info.hash(); - for (auto it = texture_entries_.find(hash); it != texture_entries_.end(); - ++it) { - if (it->second->pending_invalidation) { - // Whoa, we've been invalidated! Let's scavenge to cleanup and try again. - // TODO(benvanik): reuse existing texture storage. - Scavenge(); - break; - } - if (it->second->texture_info == texture_info) { - // Found in cache! - return it->second; - } - } - - // Not found, create. - auto entry = std::make_unique(); - entry->texture_info = texture_info; - entry->access_watch_handle = 0; - entry->pending_invalidation = false; - entry->handle = 0; - - // Check read buffer textures - there may be one waiting for us. - // TODO(benvanik): speed up existence check? - for (auto it = read_buffer_textures_.begin(); - it != read_buffer_textures_.end(); ++it) { - auto read_buffer_entry = *it; - if (read_buffer_entry->guest_address == texture_info.guest_address && - read_buffer_entry->block_width == texture_info.size_2d.block_width && - read_buffer_entry->block_height == texture_info.size_2d.block_height) { - // Found! Acquire the handle and remove the readbuffer entry. - read_buffer_textures_.erase(it); - entry->handle = read_buffer_entry->handle; - entry->access_watch_handle = read_buffer_entry->access_watch_handle; - delete read_buffer_entry; - // TODO(benvanik): set more texture properties? swizzle/etc? - auto entry_ptr = entry.get(); - texture_entries_.insert({hash, entry.release()}); - return entry_ptr; - } - } - - GLenum target; - switch (texture_info.dimension) { - case Dimension::k1D: - target = GL_TEXTURE_1D; - break; - case Dimension::k2D: - target = GL_TEXTURE_2D; - break; - case Dimension::k3D: - target = GL_TEXTURE_3D; - break; - case Dimension::kCube: - target = GL_TEXTURE_CUBE_MAP; - break; - } - - // Setup the base texture. - glCreateTextures(target, 1, &entry->handle); - - // TODO(benvanik): texture mip levels. - glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1); - - // Upload/convert. - bool uploaded = false; - switch (texture_info.dimension) { - case Dimension::k1D: - uploaded = UploadTexture1D(entry->handle, texture_info); - break; - case Dimension::k2D: - uploaded = UploadTexture2D(entry->handle, texture_info); - break; - case Dimension::kCube: - uploaded = UploadTextureCube(entry->handle, texture_info); - break; - case Dimension::k3D: - assert_unhandled_case(texture_info.dimension); - return nullptr; - } - if (!uploaded) { - XELOGE("Failed to convert/upload texture"); - return nullptr; - } - - // Add a write watch. If any data in the given range is touched we'll get a - // callback and evict the texture. We could reuse the storage, though the - // driver is likely in a better position to pool that kind of stuff. - entry->access_watch_handle = memory_->AddPhysicalAccessWatch( - texture_info.guest_address, texture_info.input_length, - cpu::MMIOHandler::kWatchWrite, - [](void* context_ptr, void* data_ptr, uint32_t address) { - auto self = reinterpret_cast(context_ptr); - auto touched_entry = reinterpret_cast(data_ptr); - // Clear watch handle first so we don't redundantly - // remove. - touched_entry->access_watch_handle = 0; - touched_entry->pending_invalidation = true; - // Add to pending list so Scavenge will clean it up. - self->invalidated_textures_mutex_.lock(); - self->invalidated_textures_->push_back(touched_entry); - self->invalidated_textures_mutex_.unlock(); - }, - this, entry.get()); - - // Add to map - map takes ownership. - auto entry_ptr = entry.get(); - texture_entries_.insert({hash, entry.release()}); - return entry_ptr; -} - -TextureCache::TextureEntry* TextureCache::LookupAddress(uint32_t guest_address, - uint32_t width, - uint32_t height, - TextureFormat format) { - // TODO(benvanik): worth speeding up? - for (auto it = texture_entries_.begin(); it != texture_entries_.end(); ++it) { - const auto& texture_info = it->second->texture_info; - if (texture_info.guest_address == guest_address && - texture_info.dimension == Dimension::k2D && - texture_info.size_2d.input_width == width && - texture_info.size_2d.input_height == height) { - return it->second; - } - } - return nullptr; -} - -GLuint TextureCache::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) { - return ConvertTexture(blitter, guest_address, logical_width, logical_height, - block_width, block_height, format, swap_channels, - src_texture, src_rect, dest_rect); -} - -GLuint TextureCache::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) { - const auto& config = texture_configs[uint32_t(format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled destination texture format"); - return 0; - } - - // See if we have used a texture at this address before. If we have, we can - // reuse it. - // TODO(benvanik): better lookup matching format/etc? - auto texture_entry = - LookupAddress(guest_address, block_width, block_height, format); - if (texture_entry) { - // Have existing texture. - assert_false(texture_entry->pending_invalidation); - if (config.format == GL_DEPTH_STENCIL) { - blitter->CopyDepthTexture(src_texture, src_rect, texture_entry->handle, - dest_rect); - } else { - blitter->CopyColorTexture2D(src_texture, src_rect, texture_entry->handle, - dest_rect, GL_LINEAR, swap_channels); - } - - // Setup a read/write access watch. If the game tries to touch the memory - // we were supposed to populate with this texture, then we'll actually - // populate it. - if (texture_entry->access_watch_handle) { - memory_->CancelAccessWatch(texture_entry->access_watch_handle); - texture_entry->access_watch_handle = 0; - } - - texture_entry->access_watch_handle = memory_->AddPhysicalAccessWatch( - guest_address, texture_entry->texture_info.input_length, - cpu::MMIOHandler::kWatchReadWrite, - [](void* context, void* data, uint32_t address) { - auto touched_entry = reinterpret_cast(data); - touched_entry->access_watch_handle = 0; - - // This happens. RDR resolves to a texture then upsizes it, BF1943 - // writes to a resolved texture. - // TODO (for Vulkan): Copy this texture back into system memory. - // assert_always(); - }, - nullptr, texture_entry); - - return texture_entry->handle; - } - - // Check pending read buffer textures (for multiple resolves with no - // uploads inbetween). - for (auto it = read_buffer_textures_.begin(); - it != read_buffer_textures_.end(); ++it) { - const auto& entry = *it; - if (entry->guest_address == guest_address && - entry->logical_width == logical_width && - entry->logical_height == logical_height && entry->format == format) { - // Found an existing entry - just reupload. - if (config.format == GL_DEPTH_STENCIL) { - blitter->CopyDepthTexture(src_texture, src_rect, entry->handle, - dest_rect); - } else { - blitter->CopyColorTexture2D(src_texture, src_rect, entry->handle, - dest_rect, GL_LINEAR, swap_channels); - } - return entry->handle; - } - } - - // Need to create a new texture. - // As we don't know anything about this texture, we'll add it to the - // pending readbuffer list. If nobody claims it after a certain amount - // of time we'll dump it. - auto entry = std::make_unique(); - entry->guest_address = guest_address; - entry->logical_width = logical_width; - entry->logical_height = logical_height; - entry->block_width = block_width; - entry->block_height = block_height; - entry->format = format; - - entry->access_watch_handle = memory_->AddPhysicalAccessWatch( - guest_address, block_height * block_width * 4, - cpu::MMIOHandler::kWatchReadWrite, - [](void* context, void* data, uint32_t address) { - auto entry = reinterpret_cast(data); - entry->access_watch_handle = 0; - - // This happens. RDR resolves to a texture then upsizes it, BF1943 - // writes to a resolved texture. - // TODO (for Vulkan): Copy this texture back into system memory. - // assert_always(); - }, - nullptr, entry.get()); - - glCreateTextures(GL_TEXTURE_2D, 1, &entry->handle); - glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0); - glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1); - glTextureStorage2D(entry->handle, 1, config.internal_format, logical_width, - logical_height); - if (config.format == GL_DEPTH_STENCIL) { - blitter->CopyDepthTexture(src_texture, src_rect, entry->handle, dest_rect); - } else { - blitter->CopyColorTexture2D(src_texture, src_rect, entry->handle, dest_rect, - GL_LINEAR, swap_channels); - } - - GLuint handle = entry->handle; - read_buffer_textures_.push_back(entry.release()); - return handle; -} - -void TextureCache::EvictTexture(TextureEntry* entry) { - if (entry->access_watch_handle) { - memory_->CancelAccessWatch(entry->access_watch_handle); - entry->access_watch_handle = 0; - } - - for (auto& view : entry->views) { - glMakeTextureHandleNonResidentARB(view->texture_sampler_handle); - } - glDeleteTextures(1, &entry->handle); - - uint64_t texture_hash = entry->texture_info.hash(); - for (auto it = texture_entries_.find(texture_hash); - it != texture_entries_.end(); ++it) { - if (it->second == entry) { - texture_entries_.erase(it); - break; - } - } - - delete entry; -} - -struct HostTextureInfo { - uint32_t output_length; - - union { - struct { - uint32_t output_width; - uint32_t output_pitch; - } size_1d; - struct { - uint32_t output_width; - uint32_t output_height; - uint32_t output_pitch; - } size_2d; - struct { - } size_3d; - struct { - uint32_t output_width; - uint32_t output_height; - uint32_t output_pitch; - uint32_t output_face_length; - } size_cube; - }; - - static bool Setup(const TextureInfo& guest_info, HostTextureInfo* out_info) { - auto& info = *out_info; - auto format = guest_info.format_info(); - - switch (guest_info.dimension) { - case Dimension::k1D: { - uint32_t bytes_per_block = - format->block_width * format->bits_per_pixel / 8; - uint32_t block_width = xe::round_up(guest_info.size_1d.logical_width, - format->block_width) / - format->block_width; - info.size_1d.output_width = block_width * format->block_width; - info.size_1d.output_pitch = block_width * bytes_per_block; - info.output_length = info.size_1d.output_pitch; - return true; - } - case Dimension::k2D: { - uint32_t bytes_per_block = format->block_width * format->block_height * - format->bits_per_pixel / 8; - uint32_t block_width = xe::round_up(guest_info.size_2d.logical_width, - format->block_width) / - format->block_width; - uint32_t block_height = xe::round_up(guest_info.size_2d.logical_height, - format->block_height) / - format->block_height; - info.size_2d.output_width = block_width * format->block_width; - info.size_2d.output_height = block_height * format->block_height; - info.size_2d.output_pitch = block_width * bytes_per_block; - info.output_length = info.size_2d.output_pitch * block_height; - return true; - }; - case Dimension::k3D: { - return false; - } - case Dimension::kCube: { - uint32_t bytes_per_block = format->block_width * format->block_height * - format->bits_per_pixel / 8; - uint32_t block_width = xe::round_up(guest_info.size_cube.logical_width, - format->block_width) / - format->block_width; - uint32_t block_height = - xe::round_up(guest_info.size_cube.logical_height, - format->block_height) / - format->block_height; - info.size_cube.output_width = block_width * format->block_width; - info.size_cube.output_height = block_height * format->block_height; - info.size_cube.output_pitch = block_width * bytes_per_block; - info.size_cube.output_face_length = - info.size_cube.output_pitch * block_height; - info.output_length = info.size_cube.output_face_length * 6; - return true; - } - } - return false; - } -}; - -void TextureSwap(Endian endianness, void* dest, const void* src, - size_t length) { - switch (endianness) { - case Endian::k8in16: - xe::copy_and_swap_16_aligned(dest, src, length / 2); - break; - case Endian::k8in32: - xe::copy_and_swap_32_aligned(dest, src, length / 4); - break; - case Endian::k16in32: // Swap high and low 16 bits within a 32 bit word - xe::copy_and_swap_16_in_32_aligned(dest, src, length); - break; - default: - case Endian::kUnspecified: - std::memcpy(dest, src, length); - break; - } -} - -bool TextureCache::UploadTexture1D(GLuint texture, - const TextureInfo& texture_info) { - SCOPE_profile_cpu_f("gpu"); - const auto host_address = - memory_->TranslatePhysical(texture_info.guest_address); - - const auto& config = texture_configs[uint32_t(texture_info.texture_format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled texture format"); - return false; - } - - HostTextureInfo host_info; - if (!HostTextureInfo::Setup(texture_info, &host_info)) { - assert_always("Failed to set up host texture info"); - return false; - } - - size_t unpack_length = host_info.output_length; - glTextureStorage1D(texture, 1, config.internal_format, - host_info.size_1d.output_width); - - auto allocation = scratch_buffer_->Acquire(unpack_length); - - if (!texture_info.is_tiled) { - if (texture_info.size_1d.input_pitch == host_info.size_1d.output_pitch) { - TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, - unpack_length); - } else { - assert_always(); - } - } else { - assert_always(); - } - size_t unpack_offset = allocation.offset; - scratch_buffer_->Commit(std::move(allocation)); - // TODO(benvanik): avoid flush on entire buffer by using another texture - // buffer. - scratch_buffer_->Flush(); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed()) { - glCompressedTextureSubImage1D(texture, 0, 0, host_info.size_1d.output_width, - config.format, - static_cast(unpack_length), - reinterpret_cast(unpack_offset)); - } else { - // Most of these don't seem to have an effect on compressed images. - // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - // glPixelStorei(GL_UNPACK_ALIGNMENT, texture_info.texel_pitch); - // glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_info.size_2d.input_width); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTextureSubImage1D(texture, 0, 0, host_info.size_1d.output_width, - config.format, config.type, - reinterpret_cast(unpack_offset)); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return true; -} - -bool TextureCache::UploadTexture2D(GLuint texture, - const TextureInfo& texture_info) { - SCOPE_profile_cpu_f("gpu"); - const auto host_address = - memory_->TranslatePhysical(texture_info.guest_address); - - const auto& config = - texture_configs[uint32_t(texture_info.format_info()->format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled texture format"); - return false; - } - - HostTextureInfo host_info; - if (!HostTextureInfo::Setup(texture_info, &host_info)) { - assert_always("Failed to set up host texture info"); - return false; - } - - size_t unpack_length = host_info.output_length; - glTextureStorage2D(texture, 1, config.internal_format, - host_info.size_2d.output_width, - host_info.size_2d.output_height); - - auto allocation = scratch_buffer_->Acquire(unpack_length); - - if (!texture_info.is_tiled) { - uint32_t offset_x, offset_y; - if (texture_info.has_packed_mips && - TextureInfo::GetPackedTileOffset(texture_info, &offset_x, &offset_y)) { - uint32_t bytes_per_block = texture_info.format_info()->block_width * - texture_info.format_info()->block_height * - texture_info.format_info()->bits_per_pixel / 8; - const uint8_t* src = host_address; - // TODO(gibbed): this needs checking - src += offset_y * texture_info.size_2d.input_pitch; - src += offset_x * bytes_per_block; - uint8_t* dest = reinterpret_cast(allocation.host_ptr); - uint32_t pitch = std::min(texture_info.size_2d.input_pitch, - host_info.size_2d.output_pitch); - for (uint32_t y = 0; y < std::min(texture_info.size_2d.block_height, - texture_info.size_2d.logical_height); - y++) { - TextureSwap(texture_info.endianness, dest, src, pitch); - src += texture_info.size_2d.input_pitch; - dest += host_info.size_2d.output_pitch; - } - } else if (texture_info.size_2d.input_pitch == - host_info.size_2d.output_pitch) { - // Fast path copy entire image. - TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, - unpack_length); - } else { - // Slow path copy row-by-row because strides differ. - // UNPACK_ROW_LENGTH only works for uncompressed images, and likely does - // this exact thing under the covers, so we just always do it here. - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast(allocation.host_ptr); - uint32_t pitch = std::min(texture_info.size_2d.input_pitch, - host_info.size_2d.output_pitch); - for (uint32_t y = 0; y < std::min(texture_info.size_2d.block_height, - texture_info.size_2d.logical_height); - y++) { - TextureSwap(texture_info.endianness, dest, src, pitch); - src += texture_info.size_2d.input_pitch; - dest += host_info.size_2d.output_pitch; - } - } - } else { - // Untile image. - // We could do this in a shader to speed things up, as this is pretty slow. - - // TODO(benvanik): optimize this inner loop (or work by tiles). - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast(allocation.host_ptr); - uint32_t bytes_per_block = texture_info.format_info()->block_width * - texture_info.format_info()->block_height * - texture_info.format_info()->bits_per_pixel / 8; - - // Tiled textures can be packed; get the offset into the packed texture. - uint32_t offset_x; - uint32_t offset_y; - TextureInfo::GetPackedTileOffset(texture_info, &offset_x, &offset_y); - - auto bpp = (bytes_per_block >> 2) + - ((bytes_per_block >> 1) >> (bytes_per_block >> 2)); - for (uint32_t y = 0, output_base_offset = 0; - y < std::min(texture_info.size_2d.block_height, - texture_info.size_2d.logical_height); - y++, output_base_offset += host_info.size_2d.output_pitch) { - auto input_base_offset = TextureInfo::TiledOffset2DOuter( - offset_y + y, - (texture_info.size_2d.input_width / - texture_info.format_info()->block_width), - bpp); - for (uint32_t x = 0, output_offset = output_base_offset; - x < texture_info.size_2d.block_width; - x++, output_offset += bytes_per_block) { - auto input_offset = - TextureInfo::TiledOffset2DInner(offset_x + x, offset_y + y, bpp, - input_base_offset) >> - bpp; - TextureSwap(texture_info.endianness, dest + output_offset, - src + input_offset * bytes_per_block, bytes_per_block); - } - } - } - size_t unpack_offset = allocation.offset; - scratch_buffer_->Commit(std::move(allocation)); - // TODO(benvanik): avoid flush on entire buffer by using another texture - // buffer. - scratch_buffer_->Flush(); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed()) { - glCompressedTextureSubImage2D( - texture, 0, 0, 0, host_info.size_2d.output_width, - host_info.size_2d.output_height, config.format, - static_cast(unpack_length), - reinterpret_cast(unpack_offset)); - } else { - // Most of these don't seem to have an effect on compressed images. - // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - // glPixelStorei(GL_UNPACK_ALIGNMENT, texture_info.texel_pitch); - // glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_info.size_2d.input_width); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTextureSubImage2D(texture, 0, 0, 0, host_info.size_2d.output_width, - host_info.size_2d.output_height, config.format, - config.type, reinterpret_cast(unpack_offset)); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return true; -} - -bool TextureCache::UploadTextureCube(GLuint texture, - const TextureInfo& texture_info) { - SCOPE_profile_cpu_f("gpu"); - const auto host_address = - memory_->TranslatePhysical(texture_info.guest_address); - - const auto& config = - texture_configs[uint32_t(texture_info.format_info()->format)]; - if (config.format == GL_INVALID_ENUM) { - assert_always("Unhandled texture format"); - return false; - } - - HostTextureInfo host_info; - if (!HostTextureInfo::Setup(texture_info, &host_info)) { - assert_always("Failed to set up host texture info"); - return false; - } - - size_t unpack_length = host_info.output_length; - glTextureStorage2D(texture, 1, config.internal_format, - host_info.size_cube.output_width, - host_info.size_cube.output_height); - - auto allocation = scratch_buffer_->Acquire(unpack_length); - if (!texture_info.is_tiled) { - if (texture_info.size_cube.input_pitch == - host_info.size_cube.output_pitch) { - // Fast path copy entire image. - TextureSwap(texture_info.endianness, allocation.host_ptr, host_address, - unpack_length); - } else { - // Slow path copy row-by-row because strides differ. - // UNPACK_ROW_LENGTH only works for uncompressed images, and likely does - // this exact thing under the covers, so we just always do it here. - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast(allocation.host_ptr); - for (int face = 0; face < 6; ++face) { - uint32_t pitch = std::min(texture_info.size_cube.input_pitch, - host_info.size_cube.output_pitch); - for (uint32_t y = 0; y < texture_info.size_cube.block_height; y++) { - TextureSwap(texture_info.endianness, dest, src, pitch); - src += texture_info.size_cube.input_pitch; - dest += host_info.size_cube.output_pitch; - } - } - } - } else { - // TODO(benvanik): optimize this inner loop (or work by tiles). - const uint8_t* src = host_address; - uint8_t* dest = reinterpret_cast(allocation.host_ptr); - uint32_t bytes_per_block = texture_info.format_info()->block_width * - texture_info.format_info()->block_height * - texture_info.format_info()->bits_per_pixel / 8; - // Tiled textures can be packed; get the offset into the packed texture. - uint32_t offset_x; - uint32_t offset_y; - TextureInfo::GetPackedTileOffset(texture_info, &offset_x, &offset_y); - auto bpp = (bytes_per_block >> 2) + - ((bytes_per_block >> 1) >> (bytes_per_block >> 2)); - for (int face = 0; face < 6; ++face) { - for (uint32_t y = 0, output_base_offset = 0; - y < texture_info.size_cube.block_height; - y++, output_base_offset += host_info.size_cube.output_pitch) { - auto input_base_offset = TextureInfo::TiledOffset2DOuter( - offset_y + y, - (texture_info.size_cube.input_width / - texture_info.format_info()->block_width), - bpp); - for (uint32_t x = 0, output_offset = output_base_offset; - x < texture_info.size_cube.block_width; - x++, output_offset += bytes_per_block) { - auto input_offset = - TextureInfo::TiledOffset2DInner(offset_x + x, offset_y + y, bpp, - input_base_offset) >> - bpp; - TextureSwap(texture_info.endianness, dest + output_offset, - src + input_offset * bytes_per_block, bytes_per_block); - } - } - src += texture_info.size_cube.input_face_length; - dest += host_info.size_cube.output_face_length; - } - } - size_t unpack_offset = allocation.offset; - scratch_buffer_->Commit(std::move(allocation)); - // TODO(benvanik): avoid flush on entire buffer by using another texture - // buffer. - scratch_buffer_->Flush(); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratch_buffer_->handle()); - if (texture_info.is_compressed()) { - glCompressedTextureSubImage3D( - texture, 0, 0, 0, 0, host_info.size_cube.output_width, - host_info.size_cube.output_height, 6, config.format, - static_cast(unpack_length), - reinterpret_cast(unpack_offset)); - } else { - // Most of these don't seem to have an effect on compressed images. - // glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); - // glPixelStorei(GL_UNPACK_ALIGNMENT, texture_info.texel_pitch); - // glPixelStorei(GL_UNPACK_ROW_LENGTH, texture_info.size_2d.input_width); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTextureSubImage3D(texture, 0, 0, 0, 0, host_info.size_cube.output_width, - host_info.size_cube.output_height, 6, config.format, - config.type, reinterpret_cast(unpack_offset)); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return true; -} - -} // namespace gl4 -} // namespace gpu -} // namespace xe diff --git a/src/xenia/gpu/gl4/texture_cache.h b/src/xenia/gpu/gl4/texture_cache.h deleted file mode 100644 index 4f018c329..000000000 --- a/src/xenia/gpu/gl4/texture_cache.h +++ /dev/null @@ -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 -#include -#include - -#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> 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 sampler_entries_; - std::unordered_map texture_entries_; - - std::vector read_buffer_textures_; - - std::mutex invalidated_textures_mutex_; - std::vector* invalidated_textures_; - std::vector invalidated_textures_sets_[2]; -}; - -} // namespace gl4 -} // namespace gpu -} // namespace xe - -#endif // XENIA_GPU_GL4_TEXTURE_CACHE_H_ diff --git a/src/xenia/hid/premake5.lua b/src/xenia/hid/premake5.lua index dbe4f7b81..b1044efa1 100644 --- a/src/xenia/hid/premake5.lua +++ b/src/xenia/hid/premake5.lua @@ -30,7 +30,6 @@ project("xenia-hid-demo") "xenia-hid", "xenia-hid-nop", "xenia-ui", - "xenia-ui-gl", }) filter("platforms:Linux") links({ diff --git a/src/xenia/ui/gl/blitter.cc b/src/xenia/ui/gl/blitter.cc deleted file mode 100644 index 7501758ca..000000000 --- a/src/xenia/ui/gl/blitter.cc +++ /dev/null @@ -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 - -#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(&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(src_texture_width), - src_rect.y / static_cast(src_texture_height), - src_rect.width / static_cast(src_texture_width), - src_rect.height / static_cast(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 diff --git a/src/xenia/ui/gl/blitter.h b/src/xenia/ui/gl/blitter.h deleted file mode 100644 index 3080c6aaa..000000000 --- a/src/xenia/ui/gl/blitter.h +++ /dev/null @@ -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 - -#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_ diff --git a/src/xenia/ui/gl/circular_buffer.cc b/src/xenia/ui/gl/circular_buffer.cc deleted file mode 100644 index 863b1bd2d..000000000 --- a/src/xenia/ui/gl/circular_buffer.cc +++ /dev/null @@ -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 - -#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(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 diff --git a/src/xenia/ui/gl/circular_buffer.h b/src/xenia/ui/gl/circular_buffer.h deleted file mode 100644 index 3b9c119f8..000000000 --- a/src/xenia/ui/gl/circular_buffer.h +++ /dev/null @@ -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 - -#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 allocation_cache_; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_CIRCULAR_BUFFER_H_ diff --git a/src/xenia/ui/gl/gl.h b/src/xenia/ui/gl/gl.h deleted file mode 100644 index 796248803..000000000 --- a/src/xenia/ui/gl/gl.h +++ /dev/null @@ -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_ diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc deleted file mode 100644 index afae0de6e..000000000 --- a/src/xenia/ui/gl/gl_context.cc +++ /dev/null @@ -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 - -#include -#include - -#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(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(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(&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 GLContext::Capture() { - GraphicsContextLock lock(this); - - std::unique_ptr 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 diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h deleted file mode 100644 index d71e209b2..000000000 --- a/src/xenia/ui/gl/gl_context.h +++ /dev/null @@ -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 - -#include -#include - -#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 Capture() override; - - Blitter* blitter() { return &blitter_; } - - protected: - Blitter blitter_; - std::unique_ptr 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 Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context = nullptr); - static std::unique_ptr 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_ diff --git a/src/xenia/ui/gl/gl_context_win.cc b/src/xenia/ui/gl/gl_context_win.cc deleted file mode 100644 index 0ca66146c..000000000 --- a/src/xenia/ui/gl/gl_context_win.cc +++ /dev/null @@ -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 - -#include -#include - -#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::Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context) { - auto context = - std::unique_ptr(new WGLContext(provider, target_window)); - if (!context->Initialize(share_context)) { - return nullptr; - } - context->AssertExtensionsPresent(); - return context; -} - -std::unique_ptr GLContext::CreateOffscreen( - GraphicsProvider* provider, GLContext* parent_context) { - return WGLContext::CreateOffscreen(provider, - static_cast(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(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(this); - - ClearCurrent(); - - return true; -} - -std::unique_ptr 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( - 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(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 diff --git a/src/xenia/ui/gl/gl_context_win.h b/src/xenia/ui/gl/gl_context_win.h deleted file mode 100644 index 772de4d3e..000000000 --- a/src/xenia/ui/gl/gl_context_win.h +++ /dev/null @@ -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 - -#include - -#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 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 glew_context_; - std::unique_ptr wglew_context_; -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_CONTEXT_H_ diff --git a/src/xenia/ui/gl/gl_context_x11.cc b/src/xenia/ui/gl/gl_context_x11.cc deleted file mode 100644 index fc4b3176b..000000000 --- a/src/xenia/ui/gl/gl_context_x11.cc +++ /dev/null @@ -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 - -#include -#include -#include - -#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::Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context) { - auto context = - std::unique_ptr(new GLXContext(provider, target_window)); - if (!context->Initialize(share_context)) { - return nullptr; - } - context->AssertExtensionsPresent(); - return context; -} - -std::unique_ptr GLContext::CreateOffscreen( - GraphicsProvider* provider, GLContext* parent_context) { - return GLXContext::CreateOffscreen(provider, - static_cast(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(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(this); - - ClearCurrent(); - - return true; -} - -std::unique_ptr 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( - 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(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 diff --git a/src/xenia/ui/gl/gl_context_x11.h b/src/xenia/ui/gl/gl_context_x11.h deleted file mode 100644 index 6eea7fdb7..000000000 --- a/src/xenia/ui/gl/gl_context_x11.h +++ /dev/null @@ -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 - -#include - -#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 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 glew_context_; - std::unique_ptr 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_ diff --git a/src/xenia/ui/gl/gl_immediate_drawer.cc b/src/xenia/ui/gl/gl_immediate_drawer.cc deleted file mode 100644 index 998ff7c50..000000000 --- a/src/xenia/ui/gl/gl_immediate_drawer.cc +++ /dev/null @@ -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 - -#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(gl_handle); - } - - ~GLImmediateTexture() override { - GLuint gl_handle = static_cast(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 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(width, height, filter, repeat); - if (data) { - UpdateTexture(texture.get(), data); - } - return std::unique_ptr(texture.release()); -} - -void GLImmediateDrawer::UpdateTexture(ImmediateTexture* texture, - const uint8_t* data) { - GraphicsContextLock lock(graphics_context_); - glTextureSubImage2D(static_cast(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(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(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 diff --git a/src/xenia/ui/gl/gl_immediate_drawer.h b/src/xenia/ui/gl/gl_immediate_drawer.h deleted file mode 100644 index e9e02798e..000000000 --- a/src/xenia/ui/gl/gl_immediate_drawer.h +++ /dev/null @@ -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 - -#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 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_ diff --git a/src/xenia/ui/gl/gl_provider.cc b/src/xenia/ui/gl/gl_provider.cc deleted file mode 100644 index b53bc9696..000000000 --- a/src/xenia/ui/gl/gl_provider.cc +++ /dev/null @@ -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 GLProvider::Create(Window* main_window) { - std::unique_ptr provider(new GLProvider(main_window)); - - // - - return std::unique_ptr(provider.release()); -} - -GLProvider::GLProvider(Window* main_window) : GraphicsProvider(main_window) {} - -GLProvider::~GLProvider() = default; - -std::unique_ptr GLProvider::CreateContext( - Window* target_window) { - auto share_context = main_window_->context(); - return std::unique_ptr( - GLContext::Create(this, target_window, - static_cast(share_context)) - .release()); -} - -std::unique_ptr GLProvider::CreateOffscreenContext() { - auto share_context = main_window_->context(); - return std::unique_ptr( - GLContext::CreateOffscreen(this, static_cast(share_context)) - .release()); -} - -} // namespace gl -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/gl/gl_provider.h b/src/xenia/ui/gl/gl_provider.h deleted file mode 100644 index 5c3303f32..000000000 --- a/src/xenia/ui/gl/gl_provider.h +++ /dev/null @@ -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 - -#include "xenia/ui/graphics_provider.h" - -namespace xe { -namespace ui { -namespace gl { - -class GLProvider : public GraphicsProvider { - public: - ~GLProvider() override; - - static std::unique_ptr Create(Window* main_window); - - std::unique_ptr CreateContext( - Window* target_window) override; - - std::unique_ptr CreateOffscreenContext() override; - - protected: - explicit GLProvider(Window* main_window); -}; - -} // namespace gl -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_GL_GL_PROVIDER_H_ diff --git a/src/xenia/ui/gl/gl_window_demo.cc b/src/xenia/ui/gl/gl_window_demo.cc deleted file mode 100644 index 6b39ab40f..000000000 --- a/src/xenia/ui/gl/gl_window_demo.cc +++ /dev/null @@ -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 -#include - -#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& args); - -std::unique_ptr 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); diff --git a/src/xenia/ui/gl/premake5.lua b/src/xenia/ui/gl/premake5.lua deleted file mode 100644 index c562290a8..000000000 --- a/src/xenia/ui/gl/premake5.lua +++ /dev/null @@ -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, - })