diff options
Diffstat (limited to 'src/video_core')
26 files changed, 524 insertions, 358 deletions
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 98a8b5337..7ff44f06d 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -29,8 +29,8 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { } void Fermi2D::HandleSurfaceCopy() { - LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", - static_cast<u32>(regs.operation)); + LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}", + static_cast<u32>(regs.operation)); // TODO(Subv): Only raw copies are implemented. ASSERT(regs.operation == Operation::SrcCopy); diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index fb3d1112c..b318aedb8 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -92,6 +92,10 @@ void Maxwell3D::InitializeRegisterDefaults() { // Some games (like Super Mario Odyssey) assume that SRGB is enabled. regs.framebuffer_srgb = 1; + mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true; + mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true; + mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; + mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; } #define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name)) @@ -256,6 +260,9 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3 // Execute the current macro. macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); + if (mme_draw.current_mode != MMEDrawMode::Undefined) { + FlushMMEInlineDraw(); + } } void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { @@ -416,6 +423,97 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { } } +void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { + if (mme_draw.current_mode == MMEDrawMode::Undefined) { + if (mme_draw.gl_begin_consume) { + mme_draw.current_mode = expected_mode; + mme_draw.current_count = count; + mme_draw.instance_count = 1; + mme_draw.gl_begin_consume = false; + mme_draw.gl_end_count = 0; + } + return; + } else { + if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count && + mme_draw.instance_mode && mme_draw.gl_begin_consume) { + mme_draw.instance_count++; + mme_draw.gl_begin_consume = false; + return; + } else { + FlushMMEInlineDraw(); + } + } + // Tail call in case it needs to retry. + StepInstance(expected_mode, count); +} + +void Maxwell3D::CallMethodFromMME(const GPU::MethodCall& method_call) { + const u32 method = method_call.method; + if (mme_inline[method]) { + regs.reg_array[method] = method_call.argument; + if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) || + method == MAXWELL3D_REG_INDEX(index_array.count)) { + const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count) + ? MMEDrawMode::Array + : MMEDrawMode::Indexed; + StepInstance(expected_mode, method_call.argument); + } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) { + mme_draw.instance_mode = + (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0); + mme_draw.gl_begin_consume = true; + } else { + mme_draw.gl_end_count++; + } + } else { + if (mme_draw.current_mode != MMEDrawMode::Undefined) { + FlushMMEInlineDraw(); + } + CallMethod(method_call); + } +} + +void Maxwell3D::FlushMMEInlineDraw() { + LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), + regs.vertex_buffer.count); + ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); + ASSERT(mme_draw.instance_count == mme_draw.gl_end_count); + + auto debug_context = system.GetGPUDebugContext(); + + if (debug_context) { + debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); + } + + // Both instance configuration registers can not be set at the same time. + ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, + "Illegal combination of instancing parameters"); + + const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; + if (ShouldExecute()) { + rasterizer.DrawMultiBatch(is_indexed); + } + + if (debug_context) { + debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); + } + + // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if + // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - + // it's possible that it is incorrect and that there is some other register used to specify the + // drawing mode. + if (is_indexed) { + regs.index_array.count = 0; + } else { + regs.vertex_buffer.count = 0; + } + mme_draw.current_mode = MMEDrawMode::Undefined; + mme_draw.current_count = 0; + mme_draw.instance_count = 0; + mme_draw.instance_mode = false; + mme_draw.gl_begin_consume = false; + mme_draw.gl_end_count = 0; +} + void Maxwell3D::ProcessMacroUpload(u32 data) { ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), "upload_address exceeded macro_memory size!"); @@ -564,7 +662,9 @@ void Maxwell3D::DrawArrays() { } const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; - rasterizer.AccelerateDrawBatch(is_indexed); + if (ShouldExecute()) { + rasterizer.DrawBatch(is_indexed); + } if (debug_context) { debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index e5ec90717..4c97759ed 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -811,8 +811,9 @@ public: INSERT_PADDING_WORDS(0x21); u32 vb_element_base; + u32 vb_base_instance; - INSERT_PADDING_WORDS(0x36); + INSERT_PADDING_WORDS(0x35); union { BitField<0, 1, u32> c0; @@ -1238,6 +1239,11 @@ public: /// Write the value to the register identified by method. void CallMethod(const GPU::MethodCall& method_call); + /// Write the value to the register identified by method. + void CallMethodFromMME(const GPU::MethodCall& method_call); + + void FlushMMEInlineDraw(); + /// Given a Texture Handle, returns the TSC and TIC entries. Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle, std::size_t offset) const; @@ -1263,6 +1269,21 @@ public: return execute_on; } + enum class MMEDrawMode : u32 { + Undefined, + Array, + Indexed, + }; + + struct MMEDrawState { + MMEDrawMode current_mode{MMEDrawMode::Undefined}; + u32 current_count{}; + u32 instance_count{}; + bool instance_mode{}; + bool gl_begin_consume{}; + u32 gl_end_count{}; + } mme_draw; + private: void InitializeRegisterDefaults(); @@ -1275,6 +1296,8 @@ private: /// Start offsets of each macro in macro_memory std::array<u32, 0x80> macro_positions = {}; + std::array<bool, Regs::NUM_REGS> mme_inline{}; + /// Memory for macro code MacroMemory macro_memory; @@ -1346,6 +1369,9 @@ private: /// Handles a write to the VERTEX_END_GL register, triggering a draw. void DrawArrays(); + + // Handles a instance drawcall from MME + void StepInstance(MMEDrawMode expected_mode, u32 count); }; #define ASSERT_REG_POSITION(field_name, position) \ @@ -1402,6 +1428,7 @@ ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); ASSERT_REG_POSITION(screen_y_control, 0x4EB); ASSERT_REG_POSITION(vb_element_base, 0x50D); +ASSERT_REG_POSITION(vb_base_instance, 0x50E); ASSERT_REG_POSITION(clip_distance_enabled, 0x544); ASSERT_REG_POSITION(point_size, 0x546); ASSERT_REG_POSITION(zeta_enable, 0x54E); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 28272ef6f..7a6355ce2 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -544,7 +544,7 @@ enum class VoteOperation : u64 { Eq = 2, // allThreadsEqualNV }; -enum class ImageAtomicSize : u64 { +enum class ImageAtomicOperationType : u64 { U32 = 0, S32 = 1, U64 = 2, @@ -1432,11 +1432,11 @@ union Instruction { ASSERT(mode == SurfaceDataMode::D_BA); return store_data_layout; } - } sust; + } suldst; union { BitField<28, 1, u64> is_ba; - BitField<51, 3, ImageAtomicSize> size; + BitField<51, 3, ImageAtomicOperationType> operation_type; BitField<33, 3, ImageType> image_type; BitField<29, 4, ImageAtomicOperation> operation; BitField<49, 2, OutOfBoundsStore> out_of_bounds_store; @@ -1595,6 +1595,7 @@ public: TMML_B, // Texture Mip Map Level TMML, // Texture Mip Map Level SUST, // Surface Store + SULD, // Surface Load SUATOM, // Surface Atomic Operation EXIT, NOP, @@ -1884,6 +1885,7 @@ private: INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"), INST("1101111101011---", Id::TMML, Type::Texture, "TMML"), INST("11101011001-----", Id::SUST, Type::Image, "SUST"), + INST("11101011000-----", Id::SULD, Type::Image, "SULD"), INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"), INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"), INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 2c47541cb..76cfe8107 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -122,6 +122,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { case RenderTargetFormat::RGBA16_UINT: case RenderTargetFormat::RGBA16_UNORM: case RenderTargetFormat::RGBA16_FLOAT: + case RenderTargetFormat::RGBX16_FLOAT: case RenderTargetFormat::RG32_FLOAT: case RenderTargetFormat::RG32_UINT: return 8; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 78bc0601a..29fa8e95b 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -42,6 +42,7 @@ enum class RenderTargetFormat : u32 { RGBA16_FLOAT = 0xCA, RG32_FLOAT = 0xCB, RG32_UINT = 0xCD, + RGBX16_FLOAT = 0xCE, BGRA8_UNORM = 0xCF, BGRA8_SRGB = 0xD0, RGB10_A2_UNORM = 0xD1, diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 62afc0d11..dbaeac6db 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -257,7 +257,7 @@ void MacroInterpreter::SetMethodAddress(u32 address) { } void MacroInterpreter::Send(u32 value) { - maxwell3d.CallMethod({method_address.address, value}); + maxwell3d.CallMethodFromMME({method_address.address, value}); // Increment the method address by the method increment. method_address.address.Assign(method_address.address.Value() + method_address.increment.Value()); diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp index 084f85e67..ab71870ab 100644 --- a/src/video_core/morton.cpp +++ b/src/video_core/morton.cpp @@ -83,6 +83,7 @@ static constexpr ConversionArray morton_to_linear_fns = { MortonCopy<true, PixelFormat::RG8U>, MortonCopy<true, PixelFormat::RG8S>, MortonCopy<true, PixelFormat::RG32UI>, + MortonCopy<true, PixelFormat::RGBX16F>, MortonCopy<true, PixelFormat::R32UI>, MortonCopy<true, PixelFormat::ASTC_2D_8X8>, MortonCopy<true, PixelFormat::ASTC_2D_8X5>, @@ -151,6 +152,7 @@ static constexpr ConversionArray linear_to_morton_fns = { MortonCopy<false, PixelFormat::RG8U>, MortonCopy<false, PixelFormat::RG8S>, MortonCopy<false, PixelFormat::RG32UI>, + MortonCopy<false, PixelFormat::RGBX16F>, MortonCopy<false, PixelFormat::R32UI>, nullptr, nullptr, diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 6b3f2d50a..5b0eca9e2 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -29,7 +29,10 @@ public: virtual ~RasterizerInterface() {} /// Draw the current batch of vertex arrays - virtual void DrawArrays() = 0; + virtual bool DrawBatch(bool is_indexed) = 0; + + /// Draw the current batch of multiple instances of vertex arrays + virtual bool DrawMultiBatch(bool is_indexed) = 0; /// Clear the current framebuffer virtual void Clear() = 0; @@ -69,10 +72,6 @@ public: return false; } - virtual bool AccelerateDrawBatch(bool is_indexed) { - return false; - } - /// Increase/decrease the number of object in pages touching the specified region virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 4f59a87b4..64de7e425 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <array> #include <cstddef> +#include <vector> #include <glad/glad.h> #include "common/logging/log.h" @@ -30,9 +32,27 @@ bool TestProgram(const GLchar* glsl) { return link_status == GL_TRUE; } +std::vector<std::string_view> GetExtensions() { + GLint num_extensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + std::vector<std::string_view> extensions; + extensions.reserve(num_extensions); + for (GLint index = 0; index < num_extensions; ++index) { + extensions.push_back( + reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(index)))); + } + return extensions; +} + +bool HasExtension(const std::vector<std::string_view>& images, std::string_view extension) { + return std::find(images.begin(), images.end(), extension) != images.end(); +} + } // Anonymous namespace Device::Device() { + const std::vector extensions = GetExtensions(); + uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); @@ -40,6 +60,7 @@ Device::Device() { has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group && GLAD_GL_NV_shader_thread_shuffle; has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; + has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); has_variable_aoffi = TestVariableAoffi(); has_component_indexing_bug = TestComponentIndexingBug(); has_precise_bug = TestPreciseBug(); @@ -55,6 +76,7 @@ Device::Device(std::nullptr_t) { max_varyings = 15; has_warp_intrinsics = true; has_vertex_viewport_layer = true; + has_image_load_formatted = true; has_variable_aoffi = true; has_component_indexing_bug = false; has_precise_bug = false; diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index ba6dcd3be..bb273c3d6 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -38,6 +38,10 @@ public: return has_vertex_viewport_layer; } + bool HasImageLoadFormatted() const { + return has_image_load_formatted; + } + bool HasVariableAoffi() const { return has_variable_aoffi; } @@ -61,6 +65,7 @@ private: u32 max_varyings{}; bool has_warp_intrinsics{}; bool has_vertex_viewport_layer{}; + bool has_image_load_formatted{}; bool has_variable_aoffi{}; bool has_component_indexing_bug{}; bool has_precise_bug{}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a2c1473db..6a17bed72 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -49,40 +49,6 @@ MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); -struct DrawParameters { - GLenum primitive_mode; - GLsizei count; - GLint current_instance; - bool use_indexed; - - GLint vertex_first; - - GLenum index_format; - GLint base_vertex; - GLintptr index_buffer_offset; - - void DispatchDraw() const { - if (use_indexed) { - const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); - if (current_instance > 0) { - glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, - index_buffer_ptr, 1, base_vertex, - current_instance); - } else { - glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, - base_vertex); - } - } else { - if (current_instance > 0) { - glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1, - current_instance); - } else { - glDrawArrays(primitive_mode, vertex_first, count); - } - } - } -}; - static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, const GLShader::ConstBufferEntry& entry) { if (!entry.IsIndirect()) { @@ -270,29 +236,6 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() { return offset; } -DrawParameters RasterizerOpenGL::SetupDraw(GLintptr index_buffer_offset) { - const auto& gpu = system.GPU().Maxwell3D(); - const auto& regs = gpu.regs; - const bool is_indexed = accelerate_draw == AccelDraw::Indexed; - - DrawParameters params{}; - params.current_instance = gpu.state.current_instance; - - params.use_indexed = is_indexed; - params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); - - if (is_indexed) { - params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); - params.count = regs.index_array.count; - params.index_buffer_offset = index_buffer_offset; - params.base_vertex = static_cast<GLint>(regs.vb_element_base); - } else { - params.count = regs.vertex_buffer.count; - params.vertex_first = regs.vertex_buffer.first; - } - return params; -} - void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { MICROPROFILE_SCOPE(OpenGL_Shader); auto& gpu = system.GPU().Maxwell3D(); @@ -399,12 +342,6 @@ std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); } -bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { - accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; - DrawArrays(); - return true; -} - template <typename Map, typename Interval> static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { return boost::make_iterator_range(map.equal_range(interval)); @@ -640,17 +577,9 @@ void RasterizerOpenGL::Clear() { } } -void RasterizerOpenGL::DrawArrays() { - if (accelerate_draw == AccelDraw::Disabled) - return; - - MICROPROFILE_SCOPE(OpenGL_Drawing); +void RasterizerOpenGL::DrawPrelude() { auto& gpu = system.GPU().Maxwell3D(); - if (!gpu.ShouldExecute()) { - return; - } - SyncColorMask(); SyncFragmentColorClampState(); SyncMultiSampleState(); @@ -695,10 +624,7 @@ void RasterizerOpenGL::DrawArrays() { // Upload vertex and index data. SetupVertexBuffer(vao); SetupVertexInstances(vao); - const GLintptr index_buffer_offset = SetupIndexBuffer(); - - // Setup draw parameters. It will automatically choose what glDraw* method to use. - const DrawParameters params = SetupDraw(index_buffer_offset); + index_buffer_offset = SetupIndexBuffer(); // Prepare packed bindings. bind_ubo_pushbuffer.Setup(0); @@ -706,7 +632,8 @@ void RasterizerOpenGL::DrawArrays() { // Setup shaders and their used resources. texture_cache.GuardSamplers(true); - SetupShaders(params.primitive_mode); + const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology); + SetupShaders(primitive_mode); texture_cache.GuardSamplers(false); ConfigureFramebuffers(); @@ -730,11 +657,107 @@ void RasterizerOpenGL::DrawArrays() { if (texture_cache.TextureBarrier()) { glTextureBarrier(); } +} + +struct DrawParams { + bool is_indexed{}; + bool is_instanced{}; + GLenum primitive_mode{}; + GLint count{}; + GLint base_vertex{}; + + // Indexed settings + GLenum index_format{}; + GLintptr index_buffer_offset{}; + + // Instanced setting + GLint num_instances{}; + GLint base_instance{}; + + void DispatchDraw() { + if (is_indexed) { + const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); + if (is_instanced) { + glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, + index_buffer_ptr, num_instances, + base_vertex, base_instance); + } else { + glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, + base_vertex); + } + } else { + if (is_instanced) { + glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances, + base_instance); + } else { + glDrawArrays(primitive_mode, base_vertex, count); + } + } + } +}; - params.DispatchDraw(); +bool RasterizerOpenGL::DrawBatch(bool is_indexed) { + accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; + + MICROPROFILE_SCOPE(OpenGL_Drawing); + DrawPrelude(); + + auto& maxwell3d = system.GPU().Maxwell3D(); + const auto& regs = maxwell3d.regs; + const auto current_instance = maxwell3d.state.current_instance; + DrawParams draw_call{}; + draw_call.is_indexed = is_indexed; + draw_call.num_instances = static_cast<GLint>(1); + draw_call.base_instance = static_cast<GLint>(current_instance); + draw_call.is_instanced = current_instance > 0; + draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); + if (draw_call.is_indexed) { + draw_call.count = static_cast<GLint>(regs.index_array.count); + draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); + draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); + draw_call.index_buffer_offset = index_buffer_offset; + } else { + draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); + draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); + } + draw_call.DispatchDraw(); + + maxwell3d.dirty.memory_general = false; accelerate_draw = AccelDraw::Disabled; - gpu.dirty.memory_general = false; + return true; +} + +bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { + accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; + + MICROPROFILE_SCOPE(OpenGL_Drawing); + + DrawPrelude(); + + auto& maxwell3d = system.GPU().Maxwell3D(); + const auto& regs = maxwell3d.regs; + const auto& draw_setup = maxwell3d.mme_draw; + DrawParams draw_call{}; + draw_call.is_indexed = is_indexed; + draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count); + draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance); + draw_call.is_instanced = draw_setup.instance_count > 1; + draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); + if (draw_call.is_indexed) { + draw_call.count = static_cast<GLint>(regs.index_array.count); + draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); + draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); + draw_call.index_buffer_offset = index_buffer_offset; + } else { + draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); + draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); + } + draw_call.DispatchDraw(); + + maxwell3d.dirty.memory_general = false; + accelerate_draw = AccelDraw::Disabled; + return true; } void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 4f5c7f864..9c10ebda3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -57,7 +57,8 @@ public: ScreenInfo& info); ~RasterizerOpenGL() override; - void DrawArrays() override; + bool DrawBatch(bool is_indexed) override; + bool DrawMultiBatch(bool is_indexed) override; void Clear() override; void DispatchCompute(GPUVAddr code_addr) override; void FlushAll() override; @@ -71,7 +72,6 @@ public: const Tegra::Engines::Fermi2D::Config& copy_config) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; - bool AccelerateDrawBatch(bool is_indexed) override; void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; void LoadDiskResources(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) override; @@ -105,6 +105,9 @@ private: void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, std::size_t size); + /// Syncs all the state, shaders, render targets and textures setting before a draw call. + void DrawPrelude(); + /// Configures the current textures to use for the draw command. Returns shaders texture buffer /// usage. TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, @@ -216,7 +219,7 @@ private: GLintptr SetupIndexBuffer(); - DrawParameters SetupDraw(GLintptr index_buffer_offset); + GLintptr index_buffer_offset; void SetupShaders(GLenum primitive_mode); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 0dbc4c02f..42ca3b1bd 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -211,14 +211,14 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn const auto primitive_mode{variant.primitive_mode}; const auto texture_buffer_usage{variant.texture_buffer_usage}; - std::string source = "#version 430 core\n" - "#extension GL_ARB_separate_shader_objects : enable\n" - "#extension GL_NV_gpu_shader5 : enable\n" - "#extension GL_NV_shader_thread_group : enable\n" - "#extension GL_NV_shader_thread_shuffle : enable\n"; - if (entries.shader_viewport_layer_array) { - source += "#extension GL_ARB_shader_viewport_layer_array : enable\n"; - } + std::string source = R"(#version 430 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shader_viewport_layer_array : enable +#extension GL_EXT_shader_image_load_formatted : enable +#extension GL_NV_gpu_shader5 : enable +#extension GL_NV_shader_thread_group : enable +#extension GL_NV_shader_thread_shuffle : enable +)"; if (program_type == ProgramType::Compute) { source += "#extension GL_ARB_compute_variable_group_size : require\n"; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 76439e7ab..8fa9e6534 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -19,6 +19,7 @@ #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" +#include "video_core/shader/node.h" #include "video_core/shader/shader_ir.h" namespace OpenGL::GLShader { @@ -241,6 +242,26 @@ constexpr const char* GetTypeString(Type type) { } } +constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) { + switch (image_type) { + case Tegra::Shader::ImageType::Texture1D: + return "1D"; + case Tegra::Shader::ImageType::TextureBuffer: + return "Buffer"; + case Tegra::Shader::ImageType::Texture1DArray: + return "1DArray"; + case Tegra::Shader::ImageType::Texture2D: + return "2D"; + case Tegra::Shader::ImageType::Texture2DArray: + return "2DArray"; + case Tegra::Shader::ImageType::Texture3D: + return "3D"; + default: + UNREACHABLE(); + return "1D"; + } +} + /// Generates code to use for a swizzle operation. constexpr const char* GetSwizzle(u32 element) { constexpr std::array swizzle = {".x", ".y", ".z", ".w"}; @@ -398,8 +419,6 @@ public: usage.is_read, usage.is_written); } entries.clip_distances = ir.GetClipDistances(); - entries.shader_viewport_layer_array = - IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex()); entries.shader_length = ir.GetLength(); return entries; } @@ -462,6 +481,14 @@ private: code.AddLine("float gl_PointSize;"); } + if (ir.UsesInstanceId()) { + code.AddLine("int gl_InstanceID;"); + } + + if (ir.UsesVertexId()) { + code.AddLine("int gl_VertexID;"); + } + --code.scope; code.AddLine("}};"); code.AddNewLine(); @@ -714,42 +741,6 @@ private: void DeclareImages() { const auto& images{ir.GetImages()}; for (const auto& [offset, image] : images) { - const char* image_type = [&] { - switch (image.GetType()) { - case Tegra::Shader::ImageType::Texture1D: - return "image1D"; - case Tegra::Shader::ImageType::TextureBuffer: - return "imageBuffer"; - case Tegra::Shader::ImageType::Texture1DArray: - return "image1DArray"; - case Tegra::Shader::ImageType::Texture2D: - return "image2D"; - case Tegra::Shader::ImageType::Texture2DArray: - return "image2DArray"; - case Tegra::Shader::ImageType::Texture3D: - return "image3D"; - default: - UNREACHABLE(); - return "image1D"; - } - }(); - - const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> { - if (!image.IsSizeKnown()) { - return {"", ""}; - } - switch (image.GetSize()) { - case Tegra::Shader::ImageAtomicSize::U32: - return {"u", "r32ui, "}; - case Tegra::Shader::ImageAtomicSize::S32: - return {"i", "r32i, "}; - default: - UNIMPLEMENTED_MSG("Unimplemented atomic size={}", - static_cast<u32>(image.GetSize())); - return {"", ""}; - } - }(); - std::string qualifier = "coherent volatile"; if (image.IsRead() && !image.IsWritten()) { qualifier += " readonly"; @@ -757,9 +748,10 @@ private: qualifier += " writeonly"; } - code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform " - "{} {};", - image.GetIndex(), qualifier, image_type, GetImage(image)); + const char* format = image.IsAtomic() ? "r32ui, " : ""; + const char* type_declaration = GetImageTypeDeclaration(image.GetType()); + code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format, + image.GetIndex(), qualifier, type_declaration, GetImage(image)); } if (!images.empty()) { code.AddNewLine(); @@ -964,7 +956,7 @@ private: switch (element) { case 2: // Config pack's first value is instance_id. - return {"config_pack[0]", Type::Uint}; + return {"gl_InstanceID", Type::Int}; case 3: return {"gl_VertexID", Type::Int}; } @@ -1226,28 +1218,13 @@ private: } std::string BuildImageValues(Operation operation) { + constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"}; const auto meta{std::get<MetaImage>(operation.GetMeta())}; - const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> { - constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"}; - if (!meta.image.IsSizeKnown()) { - return {float_constructors, Type::Float}; - } - switch (meta.image.GetSize()) { - case Tegra::Shader::ImageAtomicSize::U32: - return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint}; - case Tegra::Shader::ImageAtomicSize::S32: - return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint}; - default: - UNIMPLEMENTED_MSG("Unimplemented image size={}", - static_cast<u32>(meta.image.GetSize())); - return {float_constructors, Type::Float}; - } - }(); const std::size_t values_count{meta.values.size()}; std::string expr = fmt::format("{}(", constructors.at(values_count - 1)); for (std::size_t i = 0; i < values_count; ++i) { - expr += Visit(meta.values.at(i)).As(type); + expr += Visit(meta.values.at(i)).AsUint(); if (i + 1 < values_count) { expr += ", "; } @@ -1256,29 +1233,6 @@ private: return expr; } - Expression AtomicImage(Operation operation, const char* opname) { - constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("}; - const auto meta{std::get<MetaImage>(operation.GetMeta())}; - ASSERT(meta.values.size() == 1); - ASSERT(meta.image.IsSizeKnown()); - - const auto type = [&]() { - switch (const auto size = meta.image.GetSize()) { - case Tegra::Shader::ImageAtomicSize::U32: - return Type::Uint; - case Tegra::Shader::ImageAtomicSize::S32: - return Type::Int; - default: - UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size)); - return Type::Uint; - } - }(); - - return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image), - BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)), - type}; - } - Expression Assign(Operation operation) { const Node& dest = operation[0]; const Node& src = operation[1]; @@ -1537,6 +1491,8 @@ private: case Tegra::Shader::HalfType::H1_H1: return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat}; } + UNREACHABLE(); + return {"0", Type::Int}; } Expression HMergeF32(Operation operation) { @@ -1801,6 +1757,19 @@ private: return {tmp, Type::Float}; } + Expression ImageLoad(Operation operation) { + if (!device.HasImageLoadFormatted()) { + LOG_ERROR(Render_OpenGL, + "Device lacks GL_EXT_shader_image_load_formatted, stubbing image load"); + return {"0", Type::Int}; + } + + const auto meta{std::get<MetaImage>(operation.GetMeta())}; + return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image), + BuildIntegerCoordinates(operation), GetSwizzle(meta.element)), + Type::Uint}; + } + Expression ImageStore(Operation operation) { const auto meta{std::get<MetaImage>(operation.GetMeta())}; code.AddLine("imageStore({}, {}, {});", GetImage(meta.image), @@ -1808,31 +1777,14 @@ private: return {}; } - Expression AtomicImageAdd(Operation operation) { - return AtomicImage(operation, "imageAtomicAdd"); - } - - Expression AtomicImageMin(Operation operation) { - return AtomicImage(operation, "imageAtomicMin"); - } - - Expression AtomicImageMax(Operation operation) { - return AtomicImage(operation, "imageAtomicMax"); - } - Expression AtomicImageAnd(Operation operation) { - return AtomicImage(operation, "imageAtomicAnd"); - } - - Expression AtomicImageOr(Operation operation) { - return AtomicImage(operation, "imageAtomicOr"); - } - - Expression AtomicImageXor(Operation operation) { - return AtomicImage(operation, "imageAtomicXor"); - } + template <const std::string_view& opname> + Expression AtomicImage(Operation operation) { + const auto meta{std::get<MetaImage>(operation.GetMeta())}; + ASSERT(meta.values.size() == 1); - Expression AtomicImageExchange(Operation operation) { - return AtomicImage(operation, "imageAtomicExchange"); + return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image), + BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()), + Type::Uint}; } Expression Branch(Operation operation) { @@ -2027,6 +1979,12 @@ private: Func() = delete; ~Func() = delete; + static constexpr std::string_view Add = "Add"; + static constexpr std::string_view And = "And"; + static constexpr std::string_view Or = "Or"; + static constexpr std::string_view Xor = "Xor"; + static constexpr std::string_view Exchange = "Exchange"; + static constexpr std::string_view ShuffleIndexed = "shuffleNV"; static constexpr std::string_view ShuffleUp = "shuffleUpNV"; static constexpr std::string_view ShuffleDown = "shuffleDownNV"; @@ -2164,14 +2122,14 @@ private: &GLSLDecompiler::TextureQueryLod, &GLSLDecompiler::TexelFetch, + &GLSLDecompiler::ImageLoad, &GLSLDecompiler::ImageStore, - &GLSLDecompiler::AtomicImageAdd, - &GLSLDecompiler::AtomicImageMin, - &GLSLDecompiler::AtomicImageMax, - &GLSLDecompiler::AtomicImageAnd, - &GLSLDecompiler::AtomicImageOr, - &GLSLDecompiler::AtomicImageXor, - &GLSLDecompiler::AtomicImageExchange, + + &GLSLDecompiler::AtomicImage<Func::Add>, + &GLSLDecompiler::AtomicImage<Func::And>, + &GLSLDecompiler::AtomicImage<Func::Or>, + &GLSLDecompiler::AtomicImage<Func::Xor>, + &GLSLDecompiler::AtomicImage<Func::Exchange>, &GLSLDecompiler::Branch, &GLSLDecompiler::BranchIndirect, diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 2ea02f5bf..e538dc001 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -90,7 +90,6 @@ struct ShaderEntries { std::vector<ImageEntry> images; std::vector<GlobalMemoryEntry> global_memory_entries; std::array<bool, Maxwell::NumClipDistances> clip_distances{}; - bool shader_viewport_layer_array{}; std::size_t shader_length{}; }; diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index f141c4e3b..6a7012b54 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -343,20 +343,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn u8 is_bindless{}; u8 is_written{}; u8 is_read{}; - u8 is_size_known{}; - u32 size{}; + u8 is_atomic{}; if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) || !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) || - !LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) { + !LoadObjectFromPrecompiled(is_atomic)) { return {}; } entry.entries.images.emplace_back( static_cast<std::size_t>(offset), static_cast<std::size_t>(index), static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0, - is_read != 0, - is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size)) - : std::nullopt); + is_read != 0, is_atomic != 0); } u32 global_memory_count{}; @@ -382,12 +379,6 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn } } - bool shader_viewport_layer_array{}; - if (!LoadObjectFromPrecompiled(shader_viewport_layer_array)) { - return {}; - } - entry.entries.shader_viewport_layer_array = shader_viewport_layer_array; - u64 shader_length{}; if (!LoadObjectFromPrecompiled(shader_length)) { return {}; @@ -435,14 +426,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std: return false; } for (const auto& image : entries.images) { - const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U; if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) || !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) || !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) || - !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) { + !SaveObjectToPrecompiled(static_cast<u8>(image.IsAtomic() ? 1 : 0))) { return false; } } @@ -464,10 +454,6 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std: } } - if (!SaveObjectToPrecompiled(entries.shader_viewport_layer_array)) { - return false; - } - if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) { return false; } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 4f135fe03..173b76c4e 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -97,6 +97,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI + {GL_RGB16F, GL_RGBA16, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBX16F {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 0bbbf6851..3c5acda3e 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -143,6 +143,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI + {vk::Format::eUndefined, ComponentType::Invalid, false}, // RGBX16F {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5 diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index f7fbbb6e4..77fc58f25 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -19,6 +19,7 @@ #include "video_core/engines/shader_header.h" #include "video_core/renderer_vulkan/vk_device.h" #include "video_core/renderer_vulkan/vk_shader_decompiler.h" +#include "video_core/shader/node.h" #include "video_core/shader/shader_ir.h" namespace Vulkan::VKShader { @@ -939,22 +940,17 @@ private: return {}; } - Id ImageStore(Operation operation) { - UNIMPLEMENTED(); - return {}; - } - - Id AtomicImageAdd(Operation operation) { + Id ImageLoad(Operation operation) { UNIMPLEMENTED(); return {}; } - Id AtomicImageMin(Operation operation) { + Id ImageStore(Operation operation) { UNIMPLEMENTED(); return {}; } - Id AtomicImageMax(Operation operation) { + Id AtomicImageAdd(Operation operation) { UNIMPLEMENTED(); return {}; } @@ -1440,10 +1436,9 @@ private: &SPIRVDecompiler::TextureQueryLod, &SPIRVDecompiler::TexelFetch, + &SPIRVDecompiler::ImageLoad, &SPIRVDecompiler::ImageStore, &SPIRVDecompiler::AtomicImageAdd, - &SPIRVDecompiler::AtomicImageMin, - &SPIRVDecompiler::AtomicImageMax, &SPIRVDecompiler::AtomicImageAnd, &SPIRVDecompiler::AtomicImageOr, &SPIRVDecompiler::AtomicImageXor, diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index d54fb88c9..95ec1cdd9 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp @@ -41,11 +41,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); + const auto GetCoordinates = [this, instr](Tegra::Shader::ImageType image_type) { + std::vector<Node> coords; + const std::size_t num_coords{GetImageTypeNumCoordinates(image_type)}; + coords.reserve(num_coords); + for (std::size_t i = 0; i < num_coords; ++i) { + coords.push_back(GetRegister(instr.gpr8.Value() + i)); + } + return coords; + }; + switch (opcode->get().GetId()) { + case OpCode::Id::SULD: { + UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P); + UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store != + Tegra::Shader::OutOfBoundsStore::Ignore); + + const auto type{instr.suldst.image_type}; + auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type) + : GetBindlessImage(instr.gpr39, type)}; + image.MarkRead(); + + u32 indexer = 0; + for (u32 element = 0; element < 4; ++element) { + if (!instr.suldst.IsComponentEnabled(element)) { + continue; + } + MetaImage meta{image, {}, element}; + Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type)); + SetTemporary(bb, indexer++, std::move(value)); + } + for (u32 i = 0; i < indexer; ++i) { + SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); + } + break; + } case OpCode::Id::SUST: { - UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P); - UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore); - UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store + UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P); + UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store != + Tegra::Shader::OutOfBoundsStore::Ignore); + UNIMPLEMENTED_IF(instr.suldst.component_mask_selector != 0xf); // Ensure we have RGBA std::vector<Node> values; constexpr std::size_t hardcoded_size{4}; @@ -53,58 +88,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { values.push_back(GetRegister(instr.gpr0.Value() + i)); } - std::vector<Node> coords; - const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)}; - for (std::size_t i = 0; i < num_coords; ++i) { - coords.push_back(GetRegister(instr.gpr8.Value() + i)); - } - - const auto type{instr.sust.image_type}; - auto& image{instr.sust.is_immediate ? GetImage(instr.image, type) - : GetBindlessImage(instr.gpr39, type)}; + const auto type{instr.suldst.image_type}; + auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type) + : GetBindlessImage(instr.gpr39, type)}; image.MarkWrite(); - MetaImage meta{image, values}; - bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords))); + MetaImage meta{image, std::move(values)}; + bb.push_back(Operation(OperationCode::ImageStore, meta, GetCoordinates(type))); break; } case OpCode::Id::SUATOM: { UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0); - Node value = GetRegister(instr.gpr0); - - std::vector<Node> coords; - const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)}; - for (std::size_t i = 0; i < num_coords; ++i) { - coords.push_back(GetRegister(instr.gpr8.Value() + i)); - } - const OperationCode operation_code = [instr] { - switch (instr.suatom_d.operation) { - case Tegra::Shader::ImageAtomicOperation::Add: - return OperationCode::AtomicImageAdd; - case Tegra::Shader::ImageAtomicOperation::Min: - return OperationCode::AtomicImageMin; - case Tegra::Shader::ImageAtomicOperation::Max: - return OperationCode::AtomicImageMax; - case Tegra::Shader::ImageAtomicOperation::And: - return OperationCode::AtomicImageAnd; - case Tegra::Shader::ImageAtomicOperation::Or: - return OperationCode::AtomicImageOr; - case Tegra::Shader::ImageAtomicOperation::Xor: - return OperationCode::AtomicImageXor; - case Tegra::Shader::ImageAtomicOperation::Exch: - return OperationCode::AtomicImageExchange; + switch (instr.suatom_d.operation_type) { + case Tegra::Shader::ImageAtomicOperationType::S32: + case Tegra::Shader::ImageAtomicOperationType::U32: + switch (instr.suatom_d.operation) { + case Tegra::Shader::ImageAtomicOperation::Add: + return OperationCode::AtomicImageAdd; + case Tegra::Shader::ImageAtomicOperation::And: + return OperationCode::AtomicImageAnd; + case Tegra::Shader::ImageAtomicOperation::Or: + return OperationCode::AtomicImageOr; + case Tegra::Shader::ImageAtomicOperation::Xor: + return OperationCode::AtomicImageXor; + case Tegra::Shader::ImageAtomicOperation::Exch: + return OperationCode::AtomicImageExchange; + } default: - UNIMPLEMENTED_MSG("Unimplemented operation={}", - static_cast<u32>(instr.suatom_d.operation.Value())); - return OperationCode::AtomicImageAdd; + break; } + UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", + static_cast<u64>(instr.suatom_d.operation.Value()), + static_cast<u64>(instr.suatom_d.operation_type.Value())); + return OperationCode::AtomicImageAdd; }(); - const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)}; + Node value = GetRegister(instr.gpr0); + + const auto type = instr.suatom_d.image_type; + auto& image = GetImage(instr.image, type); + image.MarkAtomic(); + MetaImage meta{image, {std::move(value)}}; - SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords))); + SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type))); break; } default: @@ -114,35 +142,32 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { return pc; } -Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size) { +Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) { const auto offset{static_cast<std::size_t>(image.index.Value())}; - if (const auto image = TryUseExistingImage(offset, type, size)) { + if (const auto image = TryUseExistingImage(offset, type)) { return *image; } const std::size_t next_index{used_images.size()}; - return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second; + return used_images.emplace(offset, Image{offset, next_index, type}).first->second; } -Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size) { +Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) { const Node image_register{GetRegister(reg)}; const auto [base_image, cbuf_index, cbuf_offset]{ TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; - if (const auto image = TryUseExistingImage(cbuf_key, type, size)) { + if (const auto image = TryUseExistingImage(cbuf_key, type)) { return *image; } const std::size_t next_index{used_images.size()}; - return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size}) + return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type}) .first->second; } -Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size) { +Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) { auto it = used_images.find(offset); if (it == used_images.end()) { return nullptr; @@ -150,14 +175,6 @@ Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, auto& image = it->second; ASSERT(image.GetType() == type); - if (size) { - // We know the size, if it's known it has to be the same as before, otherwise we can set it. - if (image.IsSizeKnown()) { - ASSERT(image.GetSize() == size); - } else { - image.SetSize(*size); - } - } return ℑ } diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index abf2cb1ab..338bab17c 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -149,10 +149,10 @@ enum class OperationCode { TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 TexelFetch, /// (MetaTexture, int[N], int) -> float4 - ImageStore, /// (MetaImage, int[N] values) -> void + ImageLoad, /// (MetaImage, int[N] coords) -> void + ImageStore, /// (MetaImage, int[N] coords) -> void + AtomicImageAdd, /// (MetaImage, int[N] coords) -> void - AtomicImageMin, /// (MetaImage, int[N] coords) -> void - AtomicImageMax, /// (MetaImage, int[N] coords) -> void AtomicImageAnd, /// (MetaImage, int[N] coords) -> void AtomicImageOr, /// (MetaImage, int[N] coords) -> void AtomicImageXor, /// (MetaImage, int[N] coords) -> void @@ -294,21 +294,18 @@ private: class Image final { public: - constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size) - : offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {} + constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type) + : offset{offset}, index{index}, type{type}, is_bindless{false} {} constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, - Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size) + Tegra::Shader::ImageType type) : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, - is_bindless{true}, size{size} {} + is_bindless{true} {} constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, - bool is_bindless, bool is_written, bool is_read, - std::optional<Tegra::Shader::ImageAtomicSize> size) + bool is_bindless, bool is_written, bool is_read, bool is_atomic) : offset{offset}, index{index}, type{type}, is_bindless{is_bindless}, - is_written{is_written}, is_read{is_read}, size{size} {} + is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {} void MarkWrite() { is_written = true; @@ -318,8 +315,10 @@ public: is_read = true; } - void SetSize(Tegra::Shader::ImageAtomicSize size_) { - size = size_; + void MarkAtomic() { + MarkWrite(); + MarkRead(); + is_atomic = true; } constexpr std::size_t GetOffset() const { @@ -346,21 +345,17 @@ public: return is_read; } - constexpr std::pair<u32, u32> GetBindlessCBuf() const { - return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; + constexpr bool IsAtomic() const { + return is_atomic; } - constexpr bool IsSizeKnown() const { - return size.has_value(); - } - - constexpr Tegra::Shader::ImageAtomicSize GetSize() const { - return size.value(); + constexpr std::pair<u32, u32> GetBindlessCBuf() const { + return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; } constexpr bool operator<(const Image& rhs) const { - return std::tie(offset, index, type, size, is_bindless) < - std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless); + return std::tie(offset, index, type, is_bindless) < + std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless); } private: @@ -370,7 +365,7 @@ private: bool is_bindless{}; bool is_written{}; bool is_read{}; - std::optional<Tegra::Shader::ImageAtomicSize> size{}; + bool is_atomic{}; }; struct GlobalMemoryBase { @@ -402,6 +397,7 @@ struct MetaTexture { struct MetaImage { const Image& image; std::vector<Node> values; + u32 element{}; }; /// Parameters that modify an operation but are not part of any particular operand diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index bbbab0bca..2c357f310 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -114,6 +114,18 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff break; } } + if (index == Attribute::Index::TessCoordInstanceIDVertexID) { + switch (element) { + case 2: + uses_instance_id = true; + break; + case 3: + uses_vertex_id = true; + break; + default: + break; + } + } if (index == Attribute::Index::ClipDistances0123 || index == Attribute::Index::ClipDistances4567) { const auto clip_index = diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 6aed9bb84..6f666ee30 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -124,6 +124,14 @@ public: return uses_point_size; } + bool UsesInstanceId() const { + return uses_instance_id; + } + + bool UsesVertexId() const { + return uses_vertex_id; + } + bool HasPhysicalAttributes() const { return uses_physical_attributes; } @@ -276,16 +284,13 @@ private: bool is_shadow); /// Accesses an image. - Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size = {}); + Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type); /// Access a bindless image sampler. - Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size = {}); + Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); /// Tries to access an existing image, updating it's state as needed - Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, - std::optional<Tegra::Shader::ImageAtomicSize> size); + Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type); /// Extracts a sequence of bits from a node Node BitfieldExtract(Node value, u32 offset, u32 bits); @@ -373,6 +378,8 @@ private: bool uses_viewport_index{}; bool uses_point_size{}; bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes + bool uses_instance_id{}; + bool uses_vertex_id{}; Tegra::Shader::Header header; }; diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 53d0142cb..250afc6d6 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -159,6 +159,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) return PixelFormat::R32UI; case Tegra::RenderTargetFormat::RG32_UINT: return PixelFormat::RG32UI; + case Tegra::RenderTargetFormat::RGBX16_FLOAT: + return PixelFormat::RGBX16F; default: LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); @@ -415,6 +417,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { case Tegra::RenderTargetFormat::RG8_SNORM: return ComponentType::SNorm; case Tegra::RenderTargetFormat::RGBA16_FLOAT: + case Tegra::RenderTargetFormat::RGBX16_FLOAT: case Tegra::RenderTargetFormat::R11G11B10_FLOAT: case Tegra::RenderTargetFormat::RGBA32_FLOAT: case Tegra::RenderTargetFormat::RG32_FLOAT: diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 19268b7cd..1e1c432a5 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -57,36 +57,37 @@ enum class PixelFormat { RG8U = 39, RG8S = 40, RG32UI = 41, - R32UI = 42, - ASTC_2D_8X8 = 43, - ASTC_2D_8X5 = 44, - ASTC_2D_5X4 = 45, - BGRA8_SRGB = 46, - DXT1_SRGB = 47, - DXT23_SRGB = 48, - DXT45_SRGB = 49, - BC7U_SRGB = 50, - ASTC_2D_4X4_SRGB = 51, - ASTC_2D_8X8_SRGB = 52, - ASTC_2D_8X5_SRGB = 53, - ASTC_2D_5X4_SRGB = 54, - ASTC_2D_5X5 = 55, - ASTC_2D_5X5_SRGB = 56, - ASTC_2D_10X8 = 57, - ASTC_2D_10X8_SRGB = 58, + RGBX16F = 42, + R32UI = 43, + ASTC_2D_8X8 = 44, + ASTC_2D_8X5 = 45, + ASTC_2D_5X4 = 46, + BGRA8_SRGB = 47, + DXT1_SRGB = 48, + DXT23_SRGB = 49, + DXT45_SRGB = 50, + BC7U_SRGB = 51, + ASTC_2D_4X4_SRGB = 52, + ASTC_2D_8X8_SRGB = 53, + ASTC_2D_8X5_SRGB = 54, + ASTC_2D_5X4_SRGB = 55, + ASTC_2D_5X5 = 56, + ASTC_2D_5X5_SRGB = 57, + ASTC_2D_10X8 = 58, + ASTC_2D_10X8_SRGB = 59, MaxColorFormat, // Depth formats - Z32F = 59, - Z16 = 60, + Z32F = 60, + Z16 = 61, MaxDepthFormat, // DepthStencil formats - Z24S8 = 61, - S8Z24 = 62, - Z32FS8 = 63, + Z24S8 = 62, + S8Z24 = 63, + Z32FS8 = 64, MaxDepthStencilFormat, @@ -166,6 +167,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{ 0, // RG8U 0, // RG8S 0, // RG32UI + 0, // RGBX16F 0, // R32UI 2, // ASTC_2D_8X8 2, // ASTC_2D_8X5 @@ -249,6 +251,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ 1, // RG8U 1, // RG8S 1, // RG32UI + 1, // RGBX16F 1, // R32UI 8, // ASTC_2D_8X8 8, // ASTC_2D_8X5 @@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ 1, // RG8U 1, // RG8S 1, // RG32UI + 1, // RGBX16F 1, // R32UI 8, // ASTC_2D_8X8 5, // ASTC_2D_8X5 @@ -399,6 +403,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ 16, // RG8U 16, // RG8S 64, // RG32UI + 64, // RGBX16F 32, // R32UI 128, // ASTC_2D_8X8 128, // ASTC_2D_8X5 @@ -489,6 +494,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table SurfaceCompression::None, // RG8U SurfaceCompression::None, // RG8S SurfaceCompression::None, // RG32UI + SurfaceCompression::None, // RGBX16F SurfaceCompression::None, // R32UI SurfaceCompression::Converted, // ASTC_2D_8X8 SurfaceCompression::Converted, // ASTC_2D_8X5 |
