diff options
Diffstat (limited to 'src/video_core')
-rw-r--r-- | src/video_core/engines/shader_bytecode.h | 29 | ||||
-rw-r--r-- | src/video_core/gpu.cpp | 2 | ||||
-rw-r--r-- | src/video_core/gpu.h | 2 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 192 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 23 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 70 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 78 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 99 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.cpp | 15 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 6 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_stream_buffer.cpp | 201 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_stream_buffer.h | 42 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 2 |
13 files changed, 399 insertions, 362 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 9f64b248b..2526ebf28 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -200,6 +200,14 @@ enum class IMinMaxExchange : u64 { XHi = 3, }; +enum class XmadMode : u64 { + None = 0, + CLo = 1, + CHi = 2, + CSfu = 3, + CBcc = 4, +}; + enum class FlowCondition : u64 { Always = 0xF, Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? @@ -457,6 +465,18 @@ union Instruction { } bra; union { + BitField<20, 16, u64> imm20_16; + BitField<36, 1, u64> product_shift_left; + BitField<37, 1, u64> merge_37; + BitField<48, 1, u64> sign_a; + BitField<49, 1, u64> sign_b; + BitField<50, 3, XmadMode> mode; + BitField<52, 1, u64> high_b; + BitField<53, 1, u64> high_a; + BitField<56, 1, u64> merge_56; + } xmad; + + union { BitField<20, 14, u64> offset; BitField<34, 5, u64> index; } cbuf34; @@ -593,6 +613,7 @@ public: IntegerSetPredicate, PredicateSetPredicate, Conversion, + Xmad, Unknown, }; @@ -782,10 +803,10 @@ private: INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"), INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), - INST("0011011-00------", Id::XMAD_IMM, Type::Arithmetic, "XMAD_IMM"), - INST("0100111---------", Id::XMAD_CR, Type::Arithmetic, "XMAD_CR"), - INST("010100010-------", Id::XMAD_RC, Type::Arithmetic, "XMAD_RC"), - INST("0101101100------", Id::XMAD_RR, Type::Arithmetic, "XMAD_RR"), + INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), + INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), + INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), + INST("0101101100------", Id::XMAD_RR, Type::Xmad, "XMAD_RR"), }; #undef INST std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 19e7f1161..6f0343888 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -46,6 +46,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { case RenderTargetFormat::RGBA32_FLOAT: case RenderTargetFormat::RGBA32_UINT: return 16; + case RenderTargetFormat::RGBA16_UINT: case RenderTargetFormat::RGBA16_FLOAT: case RenderTargetFormat::RG32_FLOAT: return 8; @@ -67,6 +68,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { case RenderTargetFormat::R16_UINT: case RenderTargetFormat::R16_SINT: case RenderTargetFormat::R16_FLOAT: + case RenderTargetFormat::RG8_UNORM: case RenderTargetFormat::RG8_SNORM: return 2; case RenderTargetFormat::R8_UNORM: diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index e008d8f26..73abb7a18 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -20,6 +20,7 @@ enum class RenderTargetFormat : u32 { NONE = 0x0, RGBA32_FLOAT = 0xC0, RGBA32_UINT = 0xC2, + RGBA16_UINT = 0xC9, RGBA16_FLOAT = 0xCA, RG32_FLOAT = 0xCB, BGRA8_UNORM = 0xCF, @@ -35,6 +36,7 @@ enum class RenderTargetFormat : u32 { R11G11B10_FLOAT = 0xE0, R32_FLOAT = 0xE5, B5G6R5_UNORM = 0xE8, + RG8_UNORM = 0xEA, RG8_SNORM = 0xEB, R16_UNORM = 0xEE, R16_SNORM = 0xEF, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 38a7b1413..52a649e2f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -36,30 +36,21 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); -RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_window{window} { +RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) + : emu_window{window}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) { // Create sampler objects for (size_t i = 0; i < texture_samplers.size(); ++i) { texture_samplers[i].Create(); state.texture_units[i].sampler = texture_samplers[i].sampler.handle; } - // Create SSBOs - for (size_t stage = 0; stage < ssbos.size(); ++stage) { - for (size_t buffer = 0; buffer < ssbos[stage].size(); ++buffer) { - ssbos[stage][buffer].Create(); - state.draw.const_buffers[stage][buffer].ssbo = ssbos[stage][buffer].handle; - } - } - GLint ext_num; glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); for (GLint i = 0; i < ext_num; i++) { const std::string_view extension{ reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))}; - if (extension == "GL_ARB_buffer_storage") { - has_ARB_buffer_storage = true; - } else if (extension == "GL_ARB_direct_state_access") { + if (extension == "GL_ARB_direct_state_access") { has_ARB_direct_state_access = true; } else if (extension == "GL_ARB_separate_shader_objects") { has_ARB_separate_shader_objects = true; @@ -86,47 +77,31 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_wind hw_vao.Create(); - stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER); - stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2); - state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.draw.vertex_buffer = stream_buffer.GetHandle(); shader_program_manager = std::make_unique<GLShader::ProgramManager>(); state.draw.shader_program = 0; state.draw.vertex_array = hw_vao.handle; state.Apply(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle()); - - for (unsigned index = 0; index < uniform_buffers.size(); ++index) { - auto& buffer = uniform_buffers[index]; - buffer.Create(); - glBindBuffer(GL_UNIFORM_BUFFER, buffer.handle); - glBufferData(GL_UNIFORM_BUFFER, sizeof(GLShader::MaxwellUniformData), nullptr, - GL_STREAM_COPY); - glBindBufferBase(GL_UNIFORM_BUFFER, index, buffer.handle); - } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle()); glEnable(GL_BLEND); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); + LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); } -RasterizerOpenGL::~RasterizerOpenGL() { - if (stream_buffer != nullptr) { - state.draw.vertex_buffer = stream_buffer->GetHandle(); - state.Apply(); - stream_buffer->Release(); - } -} +RasterizerOpenGL::~RasterizerOpenGL() {} std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_VAO); const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; state.draw.vertex_array = hw_vao.handle; - state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.draw.vertex_buffer = stream_buffer.GetHandle(); state.Apply(); // Upload all guest vertex arrays sequentially to our buffer @@ -141,16 +116,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, ASSERT(end > start); u64 size = end - start + 1; - // Copy vertex array data - Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size); + GLintptr vertex_buffer_offset; + std::tie(array_ptr, buffer_offset, vertex_buffer_offset) = + UploadMemory(array_ptr, buffer_offset, start, size); // Bind the vertex array to the buffer at the current offset. - glBindVertexBuffer(index, stream_buffer->GetHandle(), buffer_offset, vertex_array.stride); + glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset, + vertex_array.stride); ASSERT_MSG(vertex_array.divisor == 0, "Vertex buffer divisor unimplemented"); - - array_ptr += size; - buffer_offset += size; } // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. @@ -201,22 +175,12 @@ static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program return program_code; } -void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { - // Helper function for uploading uniform data - const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { - if (has_ARB_direct_state_access) { - glCopyNamedBufferSubData(stream_buffer->GetHandle(), handle, offset, 0, size); - } else { - glBindBuffer(GL_COPY_WRITE_BUFFER, handle); - glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); - } - }; - +std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); // Next available bindpoints to use when uploading the const buffers and textures to the GLSL // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. - u32 current_constbuffer_bindpoint = static_cast<u32>(uniform_buffers.size()); + u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; u32 current_texture_bindpoint = 0; for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { @@ -228,22 +192,21 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { continue; } + std::tie(buffer_ptr, buffer_offset) = + AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment)); + const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 GLShader::MaxwellUniformData ubo{}; ubo.SetFromRegs(gpu.state.shader_stages[stage]); std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); - // Flush the buffer so that the GPU can see the data we just wrote. - glFlushMappedBufferRange(GL_ARRAY_BUFFER, buffer_offset, sizeof(ubo)); - - // Upload uniform data as one UBO per stage - const GLintptr ubo_offset = buffer_offset; - copy_buffer(uniform_buffers[stage].handle, ubo_offset, - sizeof(GLShader::MaxwellUniformData)); + // Bind the buffer + glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset, + sizeof(ubo)); - buffer_ptr += sizeof(GLShader::MaxwellUniformData); - buffer_offset += sizeof(GLShader::MaxwellUniformData); + buffer_ptr += sizeof(ubo); + buffer_offset += sizeof(ubo); GLShader::ShaderSetup setup{GetShaderProgramCode(program)}; GLShader::ShaderEntries shader_resources; @@ -282,9 +245,9 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { static_cast<Maxwell::ShaderStage>(stage)); // Configure the const buffers for this shader stage. - current_constbuffer_bindpoint = - SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, - current_constbuffer_bindpoint, shader_resources.const_buffer_entries); + std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) = SetupConstBuffers( + buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, + current_constbuffer_bindpoint, shader_resources.const_buffer_entries); // Configure the textures for this shader stage. current_texture_bindpoint = @@ -299,6 +262,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { } shader_program_manager->UseTrivialGeometryShader(); + + return {buffer_ptr, buffer_offset}; } size_t RasterizerOpenGL::CalculateVertexArraysSize() const { @@ -432,6 +397,31 @@ void RasterizerOpenGL::Clear() { } } +std::pair<u8*, GLintptr> RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, + size_t alignment) { + // Align the offset, not the mapped pointer + GLintptr offset_aligned = + static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment)); + return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned}; +} + +std::tuple<u8*, GLintptr, GLintptr> RasterizerOpenGL::UploadMemory(u8* buffer_ptr, + GLintptr buffer_offset, + Tegra::GPUVAddr gpu_addr, + size_t size, size_t alignment) { + std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment); + GLintptr uploaded_offset = buffer_offset; + + const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; + const boost::optional<VAddr> cpu_addr{memory_manager->GpuToCpuAddress(gpu_addr)}; + Memory::ReadBlock(*cpu_addr, buffer_ptr, size); + + buffer_ptr += size; + buffer_offset += size; + + return {buffer_ptr, buffer_offset, uploaded_offset}; +} + void RasterizerOpenGL::DrawArrays() { if (accelerate_draw == AccelDraw::Disabled) return; @@ -456,7 +446,7 @@ void RasterizerOpenGL::DrawArrays() { const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; const unsigned vertex_num{is_indexed ? regs.index_array.count : regs.vertex_buffer.count}; - state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.draw.vertex_buffer = stream_buffer.GetHandle(); state.Apply(); size_t buffer_size = CalculateVertexArraysSize(); @@ -466,41 +456,31 @@ void RasterizerOpenGL::DrawArrays() { } // Uniform space for the 5 shader stages - buffer_size = Common::AlignUp<size_t>(buffer_size, 4) + - sizeof(GLShader::MaxwellUniformData) * Maxwell::MaxShaderStage; + buffer_size = + Common::AlignUp<size_t>(buffer_size, 4) + + (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; + + // Add space for at least 18 constant buffers + buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); u8* buffer_ptr; GLintptr buffer_offset; - std::tie(buffer_ptr, buffer_offset) = - stream_buffer->Map(static_cast<GLsizeiptr>(buffer_size), 4); + std::tie(buffer_ptr, buffer_offset, std::ignore) = + stream_buffer.Map(static_cast<GLsizeiptr>(buffer_size), 4); + u8* buffer_ptr_base = buffer_ptr; - u8* offseted_buffer; - std::tie(offseted_buffer, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); - - offseted_buffer = - reinterpret_cast<u8*>(Common::AlignUp(reinterpret_cast<size_t>(offseted_buffer), 4)); - buffer_offset = Common::AlignUp<size_t>(buffer_offset, 4); + std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); // If indexed mode, copy the index buffer GLintptr index_buffer_offset = 0; if (is_indexed) { - const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; - const boost::optional<VAddr> index_data_addr{ - memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())}; - Memory::ReadBlock(*index_data_addr, offseted_buffer, index_buffer_size); - - index_buffer_offset = buffer_offset; - offseted_buffer += index_buffer_size; - buffer_offset += index_buffer_size; + std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory( + buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size); } - offseted_buffer = - reinterpret_cast<u8*>(Common::AlignUp(reinterpret_cast<size_t>(offseted_buffer), 4)); - buffer_offset = Common::AlignUp<size_t>(buffer_offset, 4); - - SetupShaders(offseted_buffer, buffer_offset); + std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset); - stream_buffer->Unmap(); + stream_buffer.Unmap(buffer_ptr - buffer_ptr_base); shader_program_manager->ApplyTo(state); state.Apply(); @@ -647,36 +627,23 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr } } -u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program, - u32 current_bindpoint, - const std::vector<GLShader::ConstBufferEntry>& entries) { +std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers( + u8* buffer_ptr, GLintptr buffer_offset, Maxwell::ShaderStage stage, GLuint program, + u32 current_bindpoint, const std::vector<GLShader::ConstBufferEntry>& entries) { const auto& gpu = Core::System::GetInstance().GPU(); const auto& maxwell3d = gpu.Maxwell3D(); - // Reset all buffer draw state for this stage. - for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) { - buffer.bindpoint = 0; - buffer.enabled = false; - } - // Upload only the enabled buffers from the 16 constbuffers of each shader stage const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { const auto& used_buffer = entries[bindpoint]; const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; - auto& buffer_draw_state = - state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()]; if (!buffer.enabled) { continue; } - buffer_draw_state.enabled = true; - buffer_draw_state.bindpoint = current_bindpoint + bindpoint; - - boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); - size_t size = 0; if (used_buffer.IsIndirect()) { @@ -698,25 +665,26 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr size = Common::AlignUp(size, sizeof(GLvec4)); ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); - std::vector<u8> data(size); - Memory::ReadBlock(*addr, data.data(), data.size()); + GLintptr const_buffer_offset; + std::tie(buffer_ptr, buffer_offset, const_buffer_offset) = + UploadMemory(buffer_ptr, buffer_offset, buffer.address, size, + static_cast<size_t>(uniform_buffer_alignment)); - glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo); - glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint, + stream_buffer.GetHandle(), const_buffer_offset, size); // Now configure the bindpoint of the buffer inside the shader const std::string buffer_name = used_buffer.GetName(); const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str()); if (index != GL_INVALID_INDEX) { - glUniformBlockBinding(program, index, buffer_draw_state.bindpoint); + glUniformBlockBinding(program, index, current_bindpoint + bindpoint); } } state.Apply(); - return current_bindpoint + static_cast<u32>(entries.size()); + return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())}; } u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bd01dc0ae..74307f626 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -7,6 +7,7 @@ #include <array> #include <cstddef> #include <memory> +#include <tuple> #include <utility> #include <vector> #include <glad/glad.h> @@ -100,9 +101,10 @@ private: * @param entries Vector describing the buffers that are actually used in the guest shader. * @returns The next available bindpoint for use in the next shader stage. */ - u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program, - u32 current_bindpoint, - const std::vector<GLShader::ConstBufferEntry>& entries); + std::tuple<u8*, GLintptr, u32> SetupConstBuffers( + u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, + GLuint program, u32 current_bindpoint, + const std::vector<GLShader::ConstBufferEntry>& entries); /* * Configures the current textures to use for the draw command. @@ -139,7 +141,6 @@ private: /// Syncs the blend state to match the guest state void SyncBlendState(); - bool has_ARB_buffer_storage = false; bool has_ARB_direct_state_access = false; bool has_ARB_separate_shader_objects = false; bool has_ARB_vertex_attrib_binding = false; @@ -155,22 +156,24 @@ private: OGLVertexArray hw_vao; std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; - std::array<std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers>, - Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> - ssbos; static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; - std::unique_ptr<OGLStreamBuffer> stream_buffer; + OGLStreamBuffer stream_buffer; OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; + GLint uniform_buffer_alignment; size_t CalculateVertexArraysSize() const; std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset); - std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> uniform_buffers; + std::pair<u8*, GLintptr> SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); - void SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); + std::pair<u8*, GLintptr> AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); + + std::tuple<u8*, GLintptr, GLintptr> UploadMemory(u8* buffer_ptr, GLintptr buffer_offset, + Tegra::GPUVAddr gpu_addr, size_t size, + size_t alignment = 4); enum class AccelDraw { Disabled, Arrays, Indexed }; AccelDraw accelerate_draw = AccelDraw::Disabled; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 84c250c63..d635550d2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -101,6 +101,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8 {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F + {GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, false}, // R11FG11FB10F {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI @@ -134,6 +135,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 + {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S // DepthStencil formats @@ -234,32 +236,57 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), SurfaceParams::MaxPixelFormat> morton_to_gl_fns = { - MortonCopy<true, PixelFormat::ABGR8U>, MortonCopy<true, PixelFormat::ABGR8S>, - MortonCopy<true, PixelFormat::B5G6R5>, MortonCopy<true, PixelFormat::A2B10G10R10>, - MortonCopy<true, PixelFormat::A1B5G5R5>, MortonCopy<true, PixelFormat::R8>, - MortonCopy<true, PixelFormat::R8UI>, MortonCopy<true, PixelFormat::RGBA16F>, - MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, - MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, - MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, - MortonCopy<true, PixelFormat::DXN2UNORM>, MortonCopy<true, PixelFormat::DXN2SNORM>, - MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>, - MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>, - MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>, - MortonCopy<true, PixelFormat::R32F>, MortonCopy<true, PixelFormat::R16F>, - MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::R16S>, - MortonCopy<true, PixelFormat::R16UI>, MortonCopy<true, PixelFormat::R16I>, - MortonCopy<true, PixelFormat::RG16>, MortonCopy<true, PixelFormat::RG16F>, - MortonCopy<true, PixelFormat::RG16UI>, MortonCopy<true, PixelFormat::RG16I>, - MortonCopy<true, PixelFormat::RG16S>, MortonCopy<true, PixelFormat::RGB32F>, - MortonCopy<true, PixelFormat::SRGBA8>, MortonCopy<true, PixelFormat::RG8S>, - MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, - MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>, + // clang-format off + MortonCopy<true, PixelFormat::ABGR8U>, + MortonCopy<true, PixelFormat::ABGR8S>, + MortonCopy<true, PixelFormat::B5G6R5>, + MortonCopy<true, PixelFormat::A2B10G10R10>, + MortonCopy<true, PixelFormat::A1B5G5R5>, + MortonCopy<true, PixelFormat::R8>, + MortonCopy<true, PixelFormat::R8UI>, + MortonCopy<true, PixelFormat::RGBA16F>, + MortonCopy<true, PixelFormat::RGBA16UI>, + MortonCopy<true, PixelFormat::R11FG11FB10F>, + MortonCopy<true, PixelFormat::RGBA32UI>, + MortonCopy<true, PixelFormat::DXT1>, + MortonCopy<true, PixelFormat::DXT23>, + MortonCopy<true, PixelFormat::DXT45>, + MortonCopy<true, PixelFormat::DXN1>, + MortonCopy<true, PixelFormat::DXN2UNORM>, + MortonCopy<true, PixelFormat::DXN2SNORM>, + MortonCopy<true, PixelFormat::BC7U>, + MortonCopy<true, PixelFormat::ASTC_2D_4X4>, + MortonCopy<true, PixelFormat::G8R8>, + MortonCopy<true, PixelFormat::BGRA8>, + MortonCopy<true, PixelFormat::RGBA32F>, + MortonCopy<true, PixelFormat::RG32F>, + MortonCopy<true, PixelFormat::R32F>, + MortonCopy<true, PixelFormat::R16F>, + MortonCopy<true, PixelFormat::R16UNORM>, + MortonCopy<true, PixelFormat::R16S>, + MortonCopy<true, PixelFormat::R16UI>, + MortonCopy<true, PixelFormat::R16I>, + MortonCopy<true, PixelFormat::RG16>, + MortonCopy<true, PixelFormat::RG16F>, + MortonCopy<true, PixelFormat::RG16UI>, + MortonCopy<true, PixelFormat::RG16I>, + MortonCopy<true, PixelFormat::RG16S>, + MortonCopy<true, PixelFormat::RGB32F>, + MortonCopy<true, PixelFormat::SRGBA8>, + MortonCopy<true, PixelFormat::RG8U>, + MortonCopy<true, PixelFormat::RG8S>, + MortonCopy<true, PixelFormat::Z24S8>, + MortonCopy<true, PixelFormat::S8Z24>, + MortonCopy<true, PixelFormat::Z32F>, + MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z32FS8>, + // clang-format on }; static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), SurfaceParams::MaxPixelFormat> gl_to_morton_fns = { + // clang-format off MortonCopy<false, PixelFormat::ABGR8U>, MortonCopy<false, PixelFormat::ABGR8S>, MortonCopy<false, PixelFormat::B5G6R5>, @@ -268,6 +295,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU MortonCopy<false, PixelFormat::R8>, MortonCopy<false, PixelFormat::R8UI>, MortonCopy<false, PixelFormat::RGBA16F>, + MortonCopy<false, PixelFormat::RGBA16UI>, MortonCopy<false, PixelFormat::R11FG11FB10F>, MortonCopy<false, PixelFormat::RGBA32UI>, // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not @@ -297,12 +325,14 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU MortonCopy<false, PixelFormat::RG16S>, MortonCopy<false, PixelFormat::RGB32F>, MortonCopy<false, PixelFormat::SRGBA8>, + MortonCopy<false, PixelFormat::RG8U>, MortonCopy<false, PixelFormat::RG8S>, MortonCopy<false, PixelFormat::Z24S8>, MortonCopy<false, PixelFormat::S8Z24>, MortonCopy<false, PixelFormat::Z32F>, MortonCopy<false, PixelFormat::Z16>, MortonCopy<false, PixelFormat::Z32FS8>, + // clang-format on }; // Allocate an uninitialized texture of appropriate size and format for the surface diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 202257b58..4ab74342e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -31,43 +31,45 @@ struct SurfaceParams { R8 = 5, R8UI = 6, RGBA16F = 7, - R11FG11FB10F = 8, - RGBA32UI = 9, - DXT1 = 10, - DXT23 = 11, - DXT45 = 12, - DXN1 = 13, // This is also known as BC4 - DXN2UNORM = 14, - DXN2SNORM = 15, - BC7U = 16, - ASTC_2D_4X4 = 17, - G8R8 = 18, - BGRA8 = 19, - RGBA32F = 20, - RG32F = 21, - R32F = 22, - R16F = 23, - R16UNORM = 24, - R16S = 25, - R16UI = 26, - R16I = 27, - RG16 = 28, - RG16F = 29, - RG16UI = 30, - RG16I = 31, - RG16S = 32, - RGB32F = 33, - SRGBA8 = 34, - RG8S = 35, + RGBA16UI = 8, + R11FG11FB10F = 9, + RGBA32UI = 10, + DXT1 = 11, + DXT23 = 12, + DXT45 = 13, + DXN1 = 14, // This is also known as BC4 + DXN2UNORM = 15, + DXN2SNORM = 16, + BC7U = 17, + ASTC_2D_4X4 = 18, + G8R8 = 19, + BGRA8 = 20, + RGBA32F = 21, + RG32F = 22, + R32F = 23, + R16F = 24, + R16UNORM = 25, + R16S = 26, + R16UI = 27, + R16I = 28, + RG16 = 29, + RG16F = 30, + RG16UI = 31, + RG16I = 32, + RG16S = 33, + RGB32F = 34, + SRGBA8 = 35, + RG8U = 36, + RG8S = 37, MaxColorFormat, // DepthStencil formats - Z24S8 = 36, - S8Z24 = 37, - Z32F = 38, - Z16 = 39, - Z32FS8 = 40, + Z24S8 = 38, + S8Z24 = 39, + Z32F = 40, + Z16 = 41, + Z32FS8 = 42, MaxDepthStencilFormat, @@ -113,6 +115,7 @@ struct SurfaceParams { 1, // R8 1, // R8UI 1, // RGBA16F + 1, // RGBA16UI 1, // R11FG11FB10F 1, // RGBA32UI 4, // DXT1 @@ -140,6 +143,7 @@ struct SurfaceParams { 1, // RG16S 1, // RGB32F 1, // SRGBA8 + 1, // RG8U 1, // RG8S 1, // Z24S8 1, // S8Z24 @@ -165,6 +169,7 @@ struct SurfaceParams { 8, // R8 8, // R8UI 64, // RGBA16F + 64, // RGBA16UI 32, // R11FG11FB10F 128, // RGBA32UI 64, // DXT1 @@ -192,6 +197,7 @@ struct SurfaceParams { 32, // RG16S 96, // RGB32F 32, // SRGBA8 + 16, // RG8U 16, // RG8S 32, // Z24S8 32, // S8Z24 @@ -241,6 +247,8 @@ struct SurfaceParams { return PixelFormat::A2B10G10R10; case Tegra::RenderTargetFormat::RGBA16_FLOAT: return PixelFormat::RGBA16F; + case Tegra::RenderTargetFormat::RGBA16_UINT: + return PixelFormat::RGBA16UI; case Tegra::RenderTargetFormat::RGBA32_FLOAT: return PixelFormat::RGBA32F; case Tegra::RenderTargetFormat::RG32_FLOAT: @@ -265,6 +273,8 @@ struct SurfaceParams { return PixelFormat::RG16; case Tegra::RenderTargetFormat::RG16_SNORM: return PixelFormat::RG16S; + case Tegra::RenderTargetFormat::RG8_UNORM: + return PixelFormat::RG8U; case Tegra::RenderTargetFormat::RG8_SNORM: return PixelFormat::RG8S; case Tegra::RenderTargetFormat::R16_FLOAT: @@ -432,6 +442,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RG16_UNORM: case Tegra::RenderTargetFormat::R16_UNORM: case Tegra::RenderTargetFormat::B5G6R5_UNORM: + case Tegra::RenderTargetFormat::RG8_UNORM: return ComponentType::UNorm; case Tegra::RenderTargetFormat::RGBA8_SNORM: case Tegra::RenderTargetFormat::RG16_SNORM: @@ -447,6 +458,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::R32_FLOAT: return ComponentType::Float; case Tegra::RenderTargetFormat::RGBA32_UINT: + case Tegra::RenderTargetFormat::RGBA16_UINT: case Tegra::RenderTargetFormat::RG16_UINT: case Tegra::RenderTargetFormat::R8_UINT: case Tegra::RenderTargetFormat::R16_UINT: diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index d21daf28a..6834d7085 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -356,13 +356,13 @@ public: * @param reg The register to use as the source value. */ void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { - std::string dest = GetOutputAttribute(attribute) + GetSwizzle(elem); + std::string dest = GetOutputAttribute(attribute); std::string src = GetRegisterAsFloat(reg); if (!dest.empty()) { // Can happen with unknown/unimplemented output attributes, in which case we ignore the // instruction for now. - shader.AddLine(dest + " = " + src + ';'); + shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); } } @@ -376,6 +376,8 @@ public: return value; } else if (type == GLSLRegister::Type::Integer) { return "floatBitsToInt(" + value + ')'; + } else if (type == GLSLRegister::Type::UnsignedInteger) { + return "floatBitsToUint(" + value + ')'; } else { UNREACHABLE(); } @@ -1630,6 +1632,99 @@ private: } break; } + case OpCode::Type::Xmad: { + ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); + ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); + + std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; + std::string op_b; + std::string op_c; + + // TODO(bunnei): Needs to be fixed once op_a or op_b is signed + ASSERT_MSG(instr.xmad.sign_a == instr.xmad.sign_b, "Unimplemented"); + const bool is_signed{instr.xmad.sign_a == 1}; + + bool is_merge{}; + switch (opcode->GetId()) { + case OpCode::Id::XMAD_CR: { + is_merge = instr.xmad.merge_56; + op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + instr.xmad.sign_b ? GLSLRegister::Type::Integer + : GLSLRegister::Type::UnsignedInteger); + op_c += regs.GetRegisterAsInteger(instr.gpr39, 0, is_signed); + break; + } + case OpCode::Id::XMAD_RR: { + is_merge = instr.xmad.merge_37; + op_b += regs.GetRegisterAsInteger(instr.gpr20, 0, instr.xmad.sign_b); + op_c += regs.GetRegisterAsInteger(instr.gpr39, 0, is_signed); + break; + } + case OpCode::Id::XMAD_RC: { + op_b += regs.GetRegisterAsInteger(instr.gpr39, 0, instr.xmad.sign_b); + op_c += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, + is_signed ? GLSLRegister::Type::Integer + : GLSLRegister::Type::UnsignedInteger); + break; + } + case OpCode::Id::XMAD_IMM: { + is_merge = instr.xmad.merge_37; + op_b += std::to_string(instr.xmad.imm20_16); + op_c += regs.GetRegisterAsInteger(instr.gpr39, 0, is_signed); + break; + } + default: { + LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->GetName()); + UNREACHABLE(); + } + } + + // TODO(bunnei): Ensure this is right with signed operands + if (instr.xmad.high_a) { + op_a = "((" + op_a + ") >> 16)"; + } else { + op_a = "((" + op_a + ") & 0xFFFF)"; + } + + std::string src2 = '(' + op_b + ')'; // Preserve original source 2 + if (instr.xmad.high_b) { + op_b = '(' + src2 + " >> 16)"; + } else { + op_b = '(' + src2 + " & 0xFFFF)"; + } + + std::string product = '(' + op_a + " * " + op_b + ')'; + if (instr.xmad.product_shift_left) { + product = '(' + product + " << 16)"; + } + + switch (instr.xmad.mode) { + case Tegra::Shader::XmadMode::None: + break; + case Tegra::Shader::XmadMode::CLo: + op_c = "((" + op_c + ") & 0xFFFF)"; + break; + case Tegra::Shader::XmadMode::CHi: + op_c = "((" + op_c + ") >> 16)"; + break; + case Tegra::Shader::XmadMode::CBcc: + op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; + break; + default: { + LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", + static_cast<u32>(instr.xmad.mode.Value())); + UNREACHABLE(); + } + } + + std::string sum{'(' + product + " + " + op_c + ')'}; + if (is_merge) { + sum = "((" + sum + " & 0xFFFF) | (" + src2 + "<< 16))"; + } + + regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); + break; + } default: { switch (opcode->GetId()) { case OpCode::Id::EXIT: { diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 68bacd4c5..1d1975179 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -203,21 +203,6 @@ void OpenGLState::Apply() const { } } - // Constbuffers - for (std::size_t stage = 0; stage < draw.const_buffers.size(); ++stage) { - for (std::size_t buffer_id = 0; buffer_id < draw.const_buffers[stage].size(); ++buffer_id) { - const auto& current = cur_state.draw.const_buffers[stage][buffer_id]; - const auto& new_state = draw.const_buffers[stage][buffer_id]; - - if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint || - current.ssbo != new_state.ssbo) { - if (new_state.enabled) { - glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo); - } - } - } - } - // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 5c7b636e4..bdb02ba25 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -119,12 +119,6 @@ public: GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING GLuint shader_program; // GL_CURRENT_PROGRAM GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING - struct ConstBufferConfig { - bool enabled = false; - GLuint bindpoint; - GLuint ssbo; - }; - std::array<std::array<ConstBufferConfig, Regs::MaxConstBuffers>, 5> const_buffers; } draw; struct { diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index a2713e9f0..03a8ed8b7 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -9,174 +9,91 @@ #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" -class OrphanBuffer : public OGLStreamBuffer { -public: - explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {} - ~OrphanBuffer() override; - -private: - void Create(size_t size, size_t sync_subdivide) override; - void Release() override; - - std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override; - void Unmap() override; - - std::vector<u8> data; -}; - -class StorageBuffer : public OGLStreamBuffer { -public: - explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {} - ~StorageBuffer() override; - -private: - void Create(size_t size, size_t sync_subdivide) override; - void Release() override; - - std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override; - void Unmap() override; - - struct Fence { - OGLSync sync; - size_t offset; - }; - std::deque<Fence> head; - std::deque<Fence> tail; - - u8* mapped_ptr; -}; - -OGLStreamBuffer::OGLStreamBuffer(GLenum target) { - gl_target = target; -} - -GLuint OGLStreamBuffer::GetHandle() const { - return gl_buffer.handle; -} +OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent) + : gl_target(target), buffer_size(size) { + gl_buffer.Create(); + glBindBuffer(gl_target, gl_buffer.handle); -std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) { - if (storage_buffer) { - return std::make_unique<StorageBuffer>(target); + GLsizeiptr allocate_size = size; + if (target == GL_ARRAY_BUFFER) { + // On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer + // read position is near the end and is an out-of-bound access to the vertex buffer. This is + // probably a bug in the driver and is related to the usage of vec3<byte> attributes in the + // vertex array. Doubling the allocation size for the vertex buffer seems to avoid the + // crash. + allocate_size *= 2; } - return std::make_unique<OrphanBuffer>(target); -} -OrphanBuffer::~OrphanBuffer() { - Release(); + if (GLAD_GL_ARB_buffer_storage) { + persistent = true; + coherent = prefer_coherent; + GLbitfield flags = + GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); + glBufferStorage(gl_target, allocate_size, nullptr, flags); + mapped_ptr = static_cast<u8*>(glMapBufferRange( + gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); + } else { + glBufferData(gl_target, allocate_size, nullptr, GL_STREAM_DRAW); + } } -void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) { - buffer_pos = 0; - buffer_size = size; - data.resize(buffer_size); - - if (gl_buffer.handle == 0) { - gl_buffer.Create(); +OGLStreamBuffer::~OGLStreamBuffer() { + if (persistent) { glBindBuffer(gl_target, gl_buffer.handle); + glUnmapBuffer(gl_target); } - - glBufferData(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, GL_STREAM_DRAW); -} - -void OrphanBuffer::Release() { gl_buffer.Release(); } -std::pair<u8*, GLintptr> OrphanBuffer::Map(size_t size, size_t alignment) { - buffer_pos = Common::AlignUp(buffer_pos, alignment); - - if (buffer_pos + size > buffer_size) { - Create(std::max(buffer_size, size), 0); - } - - mapped_size = size; - return std::make_pair(&data[buffer_pos], static_cast<GLintptr>(buffer_pos)); -} - -void OrphanBuffer::Unmap() { - glBufferSubData(gl_target, static_cast<GLintptr>(buffer_pos), - static_cast<GLsizeiptr>(mapped_size), &data[buffer_pos]); - buffer_pos += mapped_size; -} - -StorageBuffer::~StorageBuffer() { - Release(); +GLuint OGLStreamBuffer::GetHandle() const { + return gl_buffer.handle; } -void StorageBuffer::Create(size_t size, size_t sync_subdivide) { - if (gl_buffer.handle != 0) - return; - - buffer_pos = 0; - buffer_size = size; - buffer_sync_subdivide = std::max<size_t>(sync_subdivide, 1); - - gl_buffer.Create(); - glBindBuffer(gl_target, gl_buffer.handle); - - glBufferStorage(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT); - mapped_ptr = reinterpret_cast<u8*>( - glMapBufferRange(gl_target, 0, static_cast<GLsizeiptr>(buffer_size), - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); +GLsizeiptr OGLStreamBuffer::GetSize() const { + return buffer_size; } -void StorageBuffer::Release() { - if (gl_buffer.handle == 0) - return; - - glUnmapBuffer(gl_target); - - gl_buffer.Release(); - head.clear(); - tail.clear(); -} - -std::pair<u8*, GLintptr> StorageBuffer::Map(size_t size, size_t alignment) { +std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { ASSERT(size <= buffer_size); + ASSERT(alignment <= buffer_size); + mapped_size = size; - OGLSync sync; - - buffer_pos = Common::AlignUp(buffer_pos, alignment); - size_t effective_offset = Common::AlignDown(buffer_pos, buffer_sync_subdivide); - - if (!head.empty() && - (effective_offset > head.back().offset || buffer_pos + size > buffer_size)) { - ASSERT(head.back().sync.handle == 0); - head.back().sync.Create(); + if (alignment > 0) { + buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment); } + bool invalidate = false; if (buffer_pos + size > buffer_size) { - if (!tail.empty()) { - std::swap(sync, tail.back().sync); - tail.clear(); - } - std::swap(tail, head); buffer_pos = 0; - effective_offset = 0; - } + invalidate = true; - while (!tail.empty() && buffer_pos + size > tail.front().offset) { - std::swap(sync, tail.front().sync); - tail.pop_front(); + if (persistent) { + glUnmapBuffer(gl_target); + } } - if (sync.handle != 0) { - glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); - sync.Release(); + if (invalidate | !persistent) { + GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | + (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | + (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); + mapped_ptr = static_cast<u8*>( + glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags)); + mapped_offset = buffer_pos; } - if (head.empty() || effective_offset > head.back().offset) { - head.emplace_back(); - head.back().offset = effective_offset; + return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); +} + +void OGLStreamBuffer::Unmap(GLsizeiptr size) { + ASSERT(size <= mapped_size); + + if (!coherent && size > 0) { + glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size); } - mapped_size = size; - return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos)); -} + if (!persistent) { + glUnmapBuffer(gl_target); + } -void StorageBuffer::Unmap() { - glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos), - static_cast<GLsizeiptr>(mapped_size)); - buffer_pos += mapped_size; + buffer_pos += size; } diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index e78dc5784..45592daaf 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -2,35 +2,41 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#pragma once - -#include <memory> +#include <tuple> #include <glad/glad.h> #include "common/common_types.h" #include "video_core/renderer_opengl/gl_resource_manager.h" class OGLStreamBuffer : private NonCopyable { public: - explicit OGLStreamBuffer(GLenum target); - virtual ~OGLStreamBuffer() = default; - -public: - static std::unique_ptr<OGLStreamBuffer> MakeBuffer(bool storage_buffer, GLenum target); - - virtual void Create(size_t size, size_t sync_subdivide) = 0; - virtual void Release() {} + explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false); + ~OGLStreamBuffer(); GLuint GetHandle() const; + GLsizeiptr GetSize() const; + + /* + * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes + * and the optional alignment requirement. + * If the buffer is full, the whole buffer is reallocated which invalidates old chunks. + * The return values are the pointer to the new chunk, the offset within the buffer, + * and the invalidation flag for previous chunks. + * The actual used size must be specified on unmapping the chunk. + */ + std::tuple<u8*, GLintptr, bool> Map(GLsizeiptr size, GLintptr alignment = 0); - virtual std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) = 0; - virtual void Unmap() = 0; + void Unmap(GLsizeiptr size); -protected: +private: OGLBuffer gl_buffer; GLenum gl_target; - size_t buffer_pos = 0; - size_t buffer_size = 0; - size_t buffer_sync_subdivide = 0; - size_t mapped_size = 0; + bool coherent = false; + bool persistent = false; + + GLintptr buffer_pos = 0; + GLsizeiptr buffer_size = 0; + GLintptr mapped_offset = 0; + GLsizeiptr mapped_size = 0; + u8* mapped_ptr = nullptr; }; diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 5afd20dbe..679e5ceb2 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -91,6 +91,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { switch (topology) { case Maxwell::PrimitiveTopology::Points: return GL_POINTS; + case Maxwell::PrimitiveTopology::LineStrip: + return GL_LINE_STRIP; case Maxwell::PrimitiveTopology::Triangles: return GL_TRIANGLES; case Maxwell::PrimitiveTopology::TriangleStrip: |