GPU: Implement sampling multiple textures in the generated glsl shaders.
All tested games that use a single texture show no regression. Only Texture2D textures are supported right now, each shader gets its own "tex_fs/vs/gs" sampler array to maintain independent textures between shader stages, the textures themselves are reused if possible.
This commit is contained in:
		| @@ -354,6 +354,40 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt | |||||||
|     return textures; |     return textures; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const { | ||||||
|  |     auto& shader = state.shader_stages[static_cast<size_t>(stage)]; | ||||||
|  |     auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; | ||||||
|  |     ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); | ||||||
|  |  | ||||||
|  |     GPUVAddr tex_info_address = tex_info_buffer.address + offset * sizeof(Texture::TextureHandle); | ||||||
|  |  | ||||||
|  |     ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); | ||||||
|  |  | ||||||
|  |     boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); | ||||||
|  |     Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)}; | ||||||
|  |  | ||||||
|  |     Texture::FullTextureInfo tex_info{}; | ||||||
|  |     tex_info.index = static_cast<u32>(offset); | ||||||
|  |  | ||||||
|  |     // Load the TIC data. | ||||||
|  |     if (tex_handle.tic_id != 0) { | ||||||
|  |         tex_info.enabled = true; | ||||||
|  |  | ||||||
|  |         auto tic_entry = GetTICEntry(tex_handle.tic_id); | ||||||
|  |         // TODO(Subv): Workaround for BitField's move constructor being deleted. | ||||||
|  |         std::memcpy(&tex_info.tic, &tic_entry, sizeof(tic_entry)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Load the TSC data | ||||||
|  |     if (tex_handle.tsc_id != 0) { | ||||||
|  |         auto tsc_entry = GetTSCEntry(tex_handle.tsc_id); | ||||||
|  |         // TODO(Subv): Workaround for BitField's move constructor being deleted. | ||||||
|  |         std::memcpy(&tex_info.tsc, &tsc_entry, sizeof(tsc_entry)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return tex_info; | ||||||
|  | } | ||||||
|  |  | ||||||
| u32 Maxwell3D::GetRegisterValue(u32 method) const { | u32 Maxwell3D::GetRegisterValue(u32 method) const { | ||||||
|     ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register"); |     ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register"); | ||||||
|     return regs.reg_array[method]; |     return regs.reg_array[method]; | ||||||
|   | |||||||
| @@ -664,6 +664,9 @@ public: | |||||||
|     /// Returns a list of enabled textures for the specified shader stage. |     /// Returns a list of enabled textures for the specified shader stage. | ||||||
|     std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; |     std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; | ||||||
|  |  | ||||||
|  |     /// Returns the texture information for a specific texture in a specific shader stage. | ||||||
|  |     Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; | ||||||
|  |  | ||||||
|     /// Returns whether the specified shader stage is enabled or not. |     /// Returns whether the specified shader stage is enabled or not. | ||||||
|     bool IsShaderStageEnabled(Regs::ShaderStage stage) const; |     bool IsShaderStageEnabled(Regs::ShaderStage stage) const; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -196,8 +196,10 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||||||
|     auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); |     auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); | ||||||
|     ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!"); |     ASSERT_MSG(!gpu.regs.shader_config[0].enable, "VertexA is unsupported!"); | ||||||
|  |  | ||||||
|     // Next available bindpoint to use when uploading the const buffers to the GLSL shaders. |     // Next available bindpoints to use when uploading the const buffers and textures to the GLSL | ||||||
|  |     // shaders. | ||||||
|     u32 current_constbuffer_bindpoint = 0; |     u32 current_constbuffer_bindpoint = 0; | ||||||
|  |     u32 current_texture_bindpoint = 0; | ||||||
|  |  | ||||||
|     for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { |     for (unsigned index = 1; index < Maxwell::MaxShaderProgram; ++index) { | ||||||
|         auto& shader_config = gpu.regs.shader_config[index]; |         auto& shader_config = gpu.regs.shader_config[index]; | ||||||
| @@ -258,6 +260,11 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||||||
|         current_constbuffer_bindpoint = |         current_constbuffer_bindpoint = | ||||||
|             SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, |             SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, | ||||||
|                               current_constbuffer_bindpoint, shader_resources.const_buffer_entries); |                               current_constbuffer_bindpoint, shader_resources.const_buffer_entries); | ||||||
|  |  | ||||||
|  |         // Configure the textures for this shader stage. | ||||||
|  |         current_texture_bindpoint = | ||||||
|  |             SetupTextures(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, | ||||||
|  |                           current_texture_bindpoint, shader_resources.texture_samplers); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     shader_program_manager->UseTrivialGeometryShader(); |     shader_program_manager->UseTrivialGeometryShader(); | ||||||
| @@ -341,9 +348,6 @@ void RasterizerOpenGL::DrawArrays() { | |||||||
|     // TODO(bunnei): Sync framebuffer_scale uniform here |     // TODO(bunnei): Sync framebuffer_scale uniform here | ||||||
|     // TODO(bunnei): Sync scissorbox uniform(s) here |     // TODO(bunnei): Sync scissorbox uniform(s) here | ||||||
|  |  | ||||||
|     // Sync and bind the texture surfaces |  | ||||||
|     BindTextures(); |  | ||||||
|  |  | ||||||
|     // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable |     // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable | ||||||
|     // scissor test to prevent drawing outside of the framebuffer region |     // scissor test to prevent drawing outside of the framebuffer region | ||||||
|     state.scissor.enabled = true; |     state.scissor.enabled = true; | ||||||
| @@ -447,39 +451,6 @@ void RasterizerOpenGL::DrawArrays() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void RasterizerOpenGL::BindTextures() { |  | ||||||
|     using Regs = Tegra::Engines::Maxwell3D::Regs; |  | ||||||
|     auto& maxwell3d = Core::System::GetInstance().GPU().Get3DEngine(); |  | ||||||
|  |  | ||||||
|     // Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a |  | ||||||
|     // certain number in OpenGL. We try to only use the minimum amount of host textures by not |  | ||||||
|     // keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8 |  | ||||||
|     // can be host texture id 0 if it's the only texture used in the guest shader program. |  | ||||||
|     u32 host_texture_index = 0; |  | ||||||
|     for (u32 stage = 0; stage < Regs::MaxShaderStage; ++stage) { |  | ||||||
|         ASSERT(host_texture_index < texture_samplers.size()); |  | ||||||
|         const auto textures = maxwell3d.GetStageTextures(static_cast<Regs::ShaderStage>(stage)); |  | ||||||
|         for (unsigned texture_index = 0; texture_index < textures.size(); ++texture_index) { |  | ||||||
|             const auto& texture = textures[texture_index]; |  | ||||||
|  |  | ||||||
|             if (texture.enabled) { |  | ||||||
|                 texture_samplers[host_texture_index].SyncWithConfig(texture.tsc); |  | ||||||
|                 Surface surface = res_cache.GetTextureSurface(texture); |  | ||||||
|                 if (surface != nullptr) { |  | ||||||
|                     state.texture_units[host_texture_index].texture_2d = surface->texture.handle; |  | ||||||
|                 } else { |  | ||||||
|                     // Can occur when texture addr is null or its memory is unmapped/invalid |  | ||||||
|                     state.texture_units[texture_index].texture_2d = 0; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 ++host_texture_index; |  | ||||||
|             } else { |  | ||||||
|                 state.texture_units[texture_index].texture_2d = 0; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) { | void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) { | ||||||
|     const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; |     const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | ||||||
|     switch (method) { |     switch (method) { | ||||||
| @@ -674,6 +645,44 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr | |||||||
|     return current_bindpoint + entries.size(); |     return current_bindpoint + entries.size(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, | ||||||
|  |                                     const std::vector<GLShader::SamplerEntry>& entries) { | ||||||
|  |     auto& gpu = Core::System::GetInstance().GPU(); | ||||||
|  |     auto& maxwell3d = gpu.Get3DEngine(); | ||||||
|  |  | ||||||
|  |     ASSERT_MSG(maxwell3d.IsShaderStageEnabled(stage), | ||||||
|  |                "Attempted to upload textures of disabled shader stage"); | ||||||
|  |  | ||||||
|  |     ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units), | ||||||
|  |                "Exceeded the number of active textures."); | ||||||
|  |  | ||||||
|  |     for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | ||||||
|  |         const auto& entry = entries[bindpoint]; | ||||||
|  |         u32 current_bindpoint = current_unit + bindpoint; | ||||||
|  |  | ||||||
|  |         // Bind the uniform to the sampler. | ||||||
|  |         GLint uniform = glGetUniformLocation(program, entry.GetName().c_str()); | ||||||
|  |         ASSERT(uniform != -1); | ||||||
|  |         glProgramUniform1i(program, uniform, current_bindpoint); | ||||||
|  |  | ||||||
|  |         const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); | ||||||
|  |         ASSERT(texture.enabled); | ||||||
|  |  | ||||||
|  |         texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); | ||||||
|  |         Surface surface = res_cache.GetTextureSurface(texture); | ||||||
|  |         if (surface != nullptr) { | ||||||
|  |             state.texture_units[current_bindpoint].texture_2d = surface->texture.handle; | ||||||
|  |         } else { | ||||||
|  |             // Can occur when texture addr is null or its memory is unmapped/invalid | ||||||
|  |             state.texture_units[current_bindpoint].texture_2d = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     state.Apply(); | ||||||
|  |  | ||||||
|  |     return current_unit + entries.size(); | ||||||
|  | } | ||||||
|  |  | ||||||
| void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, | void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, | ||||||
|                                                const Surface& depth_surface, bool has_stencil) { |                                                const Surface& depth_surface, bool has_stencil) { | ||||||
|     state.draw.draw_framebuffer = framebuffer.handle; |     state.draw.draw_framebuffer = framebuffer.handle; | ||||||
|   | |||||||
| @@ -80,9 +80,6 @@ private: | |||||||
|     void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, |     void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, | ||||||
|                                  bool has_stencil); |                                  bool has_stencil); | ||||||
|  |  | ||||||
|     /// Binds the required textures to OpenGL before drawing a batch. |  | ||||||
|     void BindTextures(); |  | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
|      * Configures the current constbuffers to use for the draw command. |      * Configures the current constbuffers to use for the draw command. | ||||||
|      * @param stage The shader stage to configure buffers for. |      * @param stage The shader stage to configure buffers for. | ||||||
| @@ -95,6 +92,17 @@ private: | |||||||
|                           u32 current_bindpoint, |                           u32 current_bindpoint, | ||||||
|                           const std::vector<GLShader::ConstBufferEntry>& entries); |                           const std::vector<GLShader::ConstBufferEntry>& entries); | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * Configures the current textures to use for the draw command. | ||||||
|  |      * @param stage The shader stage to configure textures for. | ||||||
|  |      * @param program The OpenGL program object that contains the specified stage. | ||||||
|  |      * @param current_unit The offset at which to start counting unused texture units. | ||||||
|  |      * @param entries Vector describing the textures that are actually used in the guest shader. | ||||||
|  |      * @returns The next available bindpoint for use in the next shader stage. | ||||||
|  |      */ | ||||||
|  |     u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program, | ||||||
|  |                       u32 current_unit, const std::vector<GLShader::SamplerEntry>& entries); | ||||||
|  |  | ||||||
|     /// Syncs the viewport to match the guest state |     /// Syncs the viewport to match the guest state | ||||||
|     void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); |     void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -425,6 +425,14 @@ public: | |||||||
|             ++const_buffer_layout; |             ++const_buffer_layout; | ||||||
|         } |         } | ||||||
|         declarations.AddNewLine(); |         declarations.AddNewLine(); | ||||||
|  |  | ||||||
|  |         // Append the sampler2D array for the used textures. | ||||||
|  |         size_t num_samplers = GetSamplers().size(); | ||||||
|  |         if (num_samplers > 0) { | ||||||
|  |             declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + | ||||||
|  |                                  std::to_string(num_samplers) + "];"); | ||||||
|  |             declarations.AddNewLine(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Returns a list of constant buffer declarations |     /// Returns a list of constant buffer declarations | ||||||
| @@ -435,6 +443,32 @@ public: | |||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Returns a list of samplers used in the shader | ||||||
|  |     std::vector<SamplerEntry> GetSamplers() const { | ||||||
|  |         return used_samplers; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if | ||||||
|  |     /// necessary. | ||||||
|  |     std::string AccessSampler(const Sampler& sampler) { | ||||||
|  |         size_t offset = static_cast<size_t>(sampler.index.Value()); | ||||||
|  |  | ||||||
|  |         // If this sampler has already been used, return the existing mapping. | ||||||
|  |         auto itr = | ||||||
|  |             std::find_if(used_samplers.begin(), used_samplers.end(), | ||||||
|  |                          [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); | ||||||
|  |  | ||||||
|  |         if (itr != used_samplers.end()) { | ||||||
|  |             return itr->GetName(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Otherwise create a new mapping for this sampler | ||||||
|  |         size_t next_index = used_samplers.size(); | ||||||
|  |         SamplerEntry entry{stage, offset, next_index}; | ||||||
|  |         used_samplers.emplace_back(entry); | ||||||
|  |         return entry.GetName(); | ||||||
|  |     } | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     /// Build GLSL conversion function, e.g. floatBitsToInt, intBitsToFloat, etc. |     /// Build GLSL conversion function, e.g. floatBitsToInt, intBitsToFloat, etc. | ||||||
|     const std::string GetGLSLConversionFunc(GLSLRegister::Type src, GLSLRegister::Type dest) const { |     const std::string GetGLSLConversionFunc(GLSLRegister::Type src, GLSLRegister::Type dest) const { | ||||||
| @@ -544,6 +578,7 @@ private: | |||||||
|     std::set<Attribute::Index> declr_input_attribute; |     std::set<Attribute::Index> declr_input_attribute; | ||||||
|     std::set<Attribute::Index> declr_output_attribute; |     std::set<Attribute::Index> declr_output_attribute; | ||||||
|     std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; |     std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; | ||||||
|  |     std::vector<SamplerEntry> used_samplers; | ||||||
|     const Maxwell3D::Regs::ShaderStage& stage; |     const Maxwell3D::Regs::ShaderStage& stage; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -563,7 +598,7 @@ public: | |||||||
|  |  | ||||||
|     /// Returns entries in the shader that are useful for external functions |     /// Returns entries in the shader that are useful for external functions | ||||||
|     ShaderEntries GetEntries() const { |     ShaderEntries GetEntries() const { | ||||||
|         return {regs.GetConstBuffersDeclarations()}; |         return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| private: | private: | ||||||
| @@ -585,12 +620,8 @@ private: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Generates code representing a texture sampler. |     /// Generates code representing a texture sampler. | ||||||
|     std::string GetSampler(const Sampler& sampler) const { |     std::string GetSampler(const Sampler& sampler) { | ||||||
|         // TODO(Subv): Support more than just texture sampler 0 |         return regs.AccessSampler(sampler); | ||||||
|         ASSERT_MSG(sampler.index == Sampler::Index::Sampler_0, "unsupported"); |  | ||||||
|         const unsigned index{static_cast<unsigned>(sampler.index.Value()) - |  | ||||||
|                              static_cast<unsigned>(Sampler::Index::Sampler_0)}; |  | ||||||
|         return "tex[" + std::to_string(index) + ']'; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -62,8 +62,6 @@ layout (std140) uniform fs_config { | |||||||
|     vec4 viewport_flip; |     vec4 viewport_flip; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| uniform sampler2D tex[32]; |  | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
|     exec_shader(); |     exec_shader(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -56,8 +56,48 @@ private: | |||||||
|     Maxwell::ShaderStage stage; |     Maxwell::ShaderStage stage; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class SamplerEntry { | ||||||
|  |     using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) | ||||||
|  |         : offset(offset), stage(stage), sampler_index(index) {} | ||||||
|  |  | ||||||
|  |     size_t GetOffset() const { | ||||||
|  |         return offset; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size_t GetIndex() const { | ||||||
|  |         return sampler_index; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Maxwell::ShaderStage GetStage() const { | ||||||
|  |         return stage; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::string GetName() const { | ||||||
|  |         return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + | ||||||
|  |                std::to_string(sampler_index) + ']'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static std::string GetArrayName(Maxwell::ShaderStage stage) { | ||||||
|  |         return TextureSamplerNames[static_cast<size_t>(stage)]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { | ||||||
|  |         "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", | ||||||
|  |     }; | ||||||
|  |     /// Offset in TSC memory from which to read the sampler object, as specified by the sampling | ||||||
|  |     /// instruction. | ||||||
|  |     size_t offset; | ||||||
|  |     Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. | ||||||
|  |     size_t sampler_index;       ///< Value used to index into the generated GLSL sampler array. | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct ShaderEntries { | struct ShaderEntries { | ||||||
|     std::vector<ConstBufferEntry> const_buffer_entries; |     std::vector<ConstBufferEntry> const_buffer_entries; | ||||||
|  |     std::vector<SamplerEntry> texture_samplers; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| using ProgramResult = std::pair<std::string, ShaderEntries>; | using ProgramResult = std::pair<std::string, ShaderEntries>; | ||||||
|   | |||||||
| @@ -32,25 +32,6 @@ void SetShaderUniformBlockBindings(GLuint shader) { | |||||||
|                                  sizeof(MaxwellUniformData)); |                                  sizeof(MaxwellUniformData)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SetShaderSamplerBindings(GLuint shader) { |  | ||||||
|     OpenGLState cur_state = OpenGLState::GetCurState(); |  | ||||||
|     GLuint old_program = std::exchange(cur_state.draw.shader_program, shader); |  | ||||||
|     cur_state.Apply(); |  | ||||||
|  |  | ||||||
|     // Set the texture samplers to correspond to different texture units |  | ||||||
|     for (u32 texture = 0; texture < NumTextureSamplers; ++texture) { |  | ||||||
|         // Set the texture samplers to correspond to different texture units |  | ||||||
|         std::string uniform_name = "tex[" + std::to_string(texture) + "]"; |  | ||||||
|         GLint uniform_tex = glGetUniformLocation(shader, uniform_name.c_str()); |  | ||||||
|         if (uniform_tex != -1) { |  | ||||||
|             glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cur_state.draw.shader_program = old_program; |  | ||||||
|     cur_state.Apply(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace Impl | } // namespace Impl | ||||||
|  |  | ||||||
| void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { | void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { | ||||||
|   | |||||||
| @@ -45,7 +45,6 @@ public: | |||||||
|         shader.Create(program_result.first.c_str(), type); |         shader.Create(program_result.first.c_str(), type); | ||||||
|         program.Create(true, shader.handle); |         program.Create(true, shader.handle); | ||||||
|         Impl::SetShaderUniformBlockBindings(program.handle); |         Impl::SetShaderUniformBlockBindings(program.handle); | ||||||
|         Impl::SetShaderSamplerBindings(program.handle); |  | ||||||
|         entries = program_result.second; |         entries = program_result.second; | ||||||
|     } |     } | ||||||
|     GLuint GetHandle() const { |     GLuint GetHandle() const { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user