2018-03-20 04:00:59 +01:00
|
|
|
// Copyright 2015 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2018-04-20 05:01:50 +02:00
|
|
|
#include <algorithm>
|
2018-09-13 02:27:43 +02:00
|
|
|
#include <array>
|
2019-07-15 03:25:13 +02:00
|
|
|
#include <bitset>
|
2018-03-20 04:00:59 +01:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2018-07-24 18:10:35 +02:00
|
|
|
#include <string_view>
|
2018-03-20 04:00:59 +01:00
|
|
|
#include <tuple>
|
|
|
|
#include <utility>
|
|
|
|
#include <glad/glad.h>
|
|
|
|
#include "common/alignment.h"
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/logging/log.h"
|
|
|
|
#include "common/math_util.h"
|
|
|
|
#include "common/microprofile.h"
|
2018-08-22 06:33:03 +02:00
|
|
|
#include "common/scope_exit.h"
|
2018-03-24 07:01:03 +01:00
|
|
|
#include "core/core.h"
|
|
|
|
#include "core/hle/kernel/process.h"
|
2019-11-26 21:19:15 +01:00
|
|
|
#include "core/memory.h"
|
2018-03-20 04:00:59 +01:00
|
|
|
#include "core/settings.h"
|
2019-07-15 03:25:13 +02:00
|
|
|
#include "video_core/engines/kepler_compute.h"
|
2018-03-24 07:01:03 +01:00
|
|
|
#include "video_core/engines/maxwell_3d.h"
|
2019-11-18 22:35:21 +01:00
|
|
|
#include "video_core/engines/shader_type.h"
|
2019-05-29 23:15:28 +02:00
|
|
|
#include "video_core/memory_manager.h"
|
2019-11-26 22:52:15 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_query_cache.h"
|
2018-03-20 04:00:59 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
2019-01-14 02:05:53 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
2018-03-25 04:38:08 +02:00
|
|
|
#include "video_core/renderer_opengl/maxwell_to_gl.h"
|
2018-03-20 04:00:59 +01:00
|
|
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
2020-05-23 01:55:38 +02:00
|
|
|
#include "video_core/shader_cache.h"
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2018-08-21 10:18:27 +02:00
|
|
|
namespace OpenGL {
|
|
|
|
|
2018-03-24 09:06:26 +01:00
|
|
|
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
2019-04-29 02:08:31 +02:00
|
|
|
|
2020-02-22 23:40:26 +01:00
|
|
|
using Tegra::Engines::ShaderType;
|
2019-04-29 02:08:31 +02:00
|
|
|
using VideoCore::Surface::PixelFormat;
|
|
|
|
using VideoCore::Surface::SurfaceTarget;
|
|
|
|
using VideoCore::Surface::SurfaceType;
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2018-11-06 19:37:10 +01:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Format Setup", MP_RGB(128, 128, 192));
|
|
|
|
MICROPROFILE_DEFINE(OpenGL_VB, "OpenGL", "Vertex Buffer Setup", MP_RGB(128, 128, 192));
|
2018-09-04 11:02:59 +02:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
|
|
|
|
MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
|
|
|
|
MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
|
|
|
|
MICROPROFILE_DEFINE(OpenGL_Texture, "OpenGL", "Texture Setup", MP_RGB(128, 128, 192));
|
|
|
|
MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(128, 128, 192));
|
2018-03-20 04:00:59 +01:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
2018-09-04 11:02:59 +02:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
|
2018-03-20 04:00:59 +01:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
2018-10-02 19:47:26 +02:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100));
|
|
|
|
|
2019-11-13 04:27:12 +01:00
|
|
|
namespace {
|
|
|
|
|
2020-05-28 22:06:22 +02:00
|
|
|
constexpr std::size_t NUM_CONST_BUFFERS_PER_STAGE = 18;
|
|
|
|
constexpr std::size_t NUM_CONST_BUFFERS_BYTES_PER_STAGE =
|
|
|
|
NUM_CONST_BUFFERS_PER_STAGE * Maxwell::MaxConstBufferSize;
|
|
|
|
constexpr std::size_t TOTAL_CONST_BUFFER_BYTES =
|
|
|
|
NUM_CONST_BUFFERS_BYTES_PER_STAGE * Maxwell::MaxShaderStage;
|
|
|
|
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
constexpr std::size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16;
|
|
|
|
constexpr std::size_t NUM_SUPPORTED_VERTEX_BINDINGS = 16;
|
2020-01-13 21:20:02 +01:00
|
|
|
|
2019-11-13 04:27:12 +01:00
|
|
|
template <typename Engine, typename Entry>
|
|
|
|
Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
|
2020-02-22 23:40:26 +01:00
|
|
|
ShaderType shader_type, std::size_t index = 0) {
|
2020-06-05 04:03:49 +02:00
|
|
|
if constexpr (std::is_same_v<Entry, SamplerEntry>) {
|
|
|
|
if (entry.is_separated) {
|
|
|
|
const u32 buffer_1 = entry.buffer;
|
|
|
|
const u32 buffer_2 = entry.secondary_buffer;
|
|
|
|
const u32 offset_1 = entry.offset;
|
|
|
|
const u32 offset_2 = entry.secondary_offset;
|
|
|
|
const u32 handle_1 = engine.AccessConstBuffer32(shader_type, buffer_1, offset_1);
|
|
|
|
const u32 handle_2 = engine.AccessConstBuffer32(shader_type, buffer_2, offset_2);
|
|
|
|
return engine.GetTextureInfo(handle_1 | handle_2);
|
|
|
|
}
|
|
|
|
}
|
2020-04-16 06:34:45 +02:00
|
|
|
if (entry.is_bindless) {
|
2020-06-05 04:03:49 +02:00
|
|
|
const u32 handle = engine.AccessConstBuffer32(shader_type, entry.buffer, entry.offset);
|
|
|
|
return engine.GetTextureInfo(handle);
|
2019-11-13 04:27:12 +01:00
|
|
|
}
|
2020-06-05 04:03:49 +02:00
|
|
|
|
2020-01-06 16:43:13 +01:00
|
|
|
const auto& gpu_profile = engine.AccessGuestDriverProfile();
|
2020-04-16 06:34:45 +02:00
|
|
|
const u32 offset = entry.offset + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize());
|
2019-11-13 04:27:12 +01:00
|
|
|
if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) {
|
2020-01-06 16:43:13 +01:00
|
|
|
return engine.GetStageTexture(shader_type, offset);
|
2019-11-13 04:27:12 +01:00
|
|
|
} else {
|
2020-01-06 16:43:13 +01:00
|
|
|
return engine.GetTexture(offset);
|
2019-11-13 04:27:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
|
2020-02-26 20:13:47 +01:00
|
|
|
const ConstBufferEntry& entry) {
|
2019-07-06 04:11:58 +02:00
|
|
|
if (!entry.IsIndirect()) {
|
|
|
|
return entry.GetSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer.size > Maxwell::MaxConstBufferSize) {
|
|
|
|
LOG_WARNING(Render_OpenGL, "Indirect constbuffer size {} exceeds maximum {}", buffer.size,
|
|
|
|
Maxwell::MaxConstBufferSize);
|
|
|
|
return Maxwell::MaxConstBufferSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer.size;
|
|
|
|
}
|
|
|
|
|
2020-06-03 07:48:41 +02:00
|
|
|
/// Translates hardware transform feedback indices
|
|
|
|
/// @param location Hardware location
|
|
|
|
/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
|
|
|
|
/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
|
|
|
|
std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
|
|
|
|
const u8 index = location / 4;
|
|
|
|
if (index >= 8 && index <= 39) {
|
|
|
|
return {GL_GENERIC_ATTRIB_NV, index - 8};
|
|
|
|
}
|
|
|
|
if (index >= 48 && index <= 55) {
|
|
|
|
return {GL_TEXTURE_COORD_NV, index - 48};
|
|
|
|
}
|
|
|
|
switch (index) {
|
|
|
|
case 7:
|
|
|
|
return {GL_POSITION, 0};
|
|
|
|
case 40:
|
|
|
|
return {GL_PRIMARY_COLOR_NV, 0};
|
|
|
|
case 41:
|
|
|
|
return {GL_SECONDARY_COLOR_NV, 0};
|
|
|
|
case 42:
|
|
|
|
return {GL_BACK_PRIMARY_COLOR_NV, 0};
|
|
|
|
case 43:
|
|
|
|
return {GL_BACK_SECONDARY_COLOR_NV, 0};
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("index={}", static_cast<int>(index));
|
|
|
|
return {GL_POSITION, 0};
|
|
|
|
}
|
|
|
|
|
2019-12-26 04:28:46 +01:00
|
|
|
void oglEnable(GLenum cap, bool state) {
|
|
|
|
(state ? glEnable : glDisable)(cap);
|
|
|
|
}
|
|
|
|
|
2019-11-13 04:27:12 +01:00
|
|
|
} // Anonymous namespace
|
|
|
|
|
2019-04-06 22:59:56 +02:00
|
|
|
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
2020-05-18 03:32:49 +02:00
|
|
|
const Device& device, ScreenInfo& info,
|
|
|
|
ProgramManager& program_manager, StateTracker& state_tracker)
|
|
|
|
: RasterizerAccelerated{system.Memory()}, device{device}, texture_cache{system, *this, device,
|
|
|
|
state_tracker},
|
2020-04-16 04:59:29 +02:00
|
|
|
shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
|
|
|
|
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
|
|
|
|
fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
|
|
|
|
screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} {
|
2018-11-23 16:11:21 +01:00
|
|
|
CheckExtensions();
|
2020-05-18 03:32:49 +02:00
|
|
|
|
2020-05-28 22:06:22 +02:00
|
|
|
unified_uniform_buffer.Create();
|
|
|
|
glNamedBufferStorage(unified_uniform_buffer.handle, TOTAL_CONST_BUFFER_BYTES, nullptr, 0);
|
|
|
|
|
2020-05-18 03:32:49 +02:00
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
glCreateBuffers(static_cast<GLsizei>(staging_cbufs.size()), staging_cbufs.data());
|
|
|
|
for (const GLuint cbuf : staging_cbufs) {
|
|
|
|
glNamedBufferStorage(cbuf, static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize),
|
|
|
|
nullptr, 0);
|
|
|
|
}
|
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
2020-05-18 03:32:49 +02:00
|
|
|
RasterizerOpenGL::~RasterizerOpenGL() {
|
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
glDeleteBuffers(static_cast<GLsizei>(staging_cbufs.size()), staging_cbufs.data());
|
|
|
|
}
|
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2018-11-23 16:11:21 +01:00
|
|
|
void RasterizerOpenGL::CheckExtensions() {
|
|
|
|
if (!GLAD_GL_ARB_texture_filter_anisotropic && !GLAD_GL_EXT_texture_filter_anisotropic) {
|
|
|
|
LOG_WARNING(
|
|
|
|
Render_OpenGL,
|
|
|
|
"Anisotropic filter is not supported! This can cause graphical issues in some games.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-26 04:16:52 +01:00
|
|
|
void RasterizerOpenGL::SetupVertexFormat() {
|
2019-03-09 07:25:11 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
2019-12-29 03:08:40 +01:00
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::VertexFormats]) {
|
|
|
|
return;
|
2019-01-06 05:53:27 +01:00
|
|
|
}
|
2019-12-29 03:08:40 +01:00
|
|
|
flags[Dirty::VertexFormats] = false;
|
2018-11-06 19:15:44 +01:00
|
|
|
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_VAO);
|
|
|
|
|
2019-12-26 04:16:52 +01:00
|
|
|
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. Enables
|
|
|
|
// the first 16 vertex attributes always, as we don't know which ones are actually used until
|
|
|
|
// shader time. Note, Tegra technically supports 32, but we're capping this to 16 for now to
|
|
|
|
// avoid OpenGL errors.
|
|
|
|
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
|
|
|
|
// assume every shader uses them all.
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
|
2019-12-29 03:08:40 +01:00
|
|
|
if (!flags[Dirty::VertexFormat0 + index]) {
|
|
|
|
continue;
|
2018-09-05 11:36:50 +02:00
|
|
|
}
|
2019-12-29 03:08:40 +01:00
|
|
|
flags[Dirty::VertexFormat0 + index] = false;
|
|
|
|
|
|
|
|
const auto attrib = gpu.regs.vertex_attrib_format[index];
|
|
|
|
const auto gl_index = static_cast<GLuint>(index);
|
2018-11-06 21:26:27 +01:00
|
|
|
|
2020-04-14 22:58:53 +02:00
|
|
|
// Disable constant attributes.
|
|
|
|
if (attrib.IsConstant()) {
|
2019-12-29 03:08:40 +01:00
|
|
|
glDisableVertexAttribArray(gl_index);
|
2019-12-26 04:16:52 +01:00
|
|
|
continue;
|
2018-09-05 11:36:50 +02:00
|
|
|
}
|
2019-12-29 03:08:40 +01:00
|
|
|
glEnableVertexAttribArray(gl_index);
|
2019-01-06 05:53:27 +01:00
|
|
|
|
2019-12-26 04:16:52 +01:00
|
|
|
if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
|
|
|
|
attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
|
2019-12-29 03:08:40 +01:00
|
|
|
glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
|
2020-06-29 17:48:38 +02:00
|
|
|
MaxwellToGL::VertexFormat(attrib), attrib.offset);
|
2019-12-26 04:16:52 +01:00
|
|
|
} else {
|
2020-06-29 17:48:38 +02:00
|
|
|
glVertexAttribFormat(gl_index, attrib.ComponentCount(),
|
|
|
|
MaxwellToGL::VertexFormat(attrib),
|
2019-12-26 04:16:52 +01:00
|
|
|
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
|
|
|
|
}
|
2019-12-29 03:08:40 +01:00
|
|
|
glVertexAttribBinding(gl_index, attrib.buffer);
|
2019-12-26 04:16:52 +01:00
|
|
|
}
|
2018-11-06 19:37:10 +01:00
|
|
|
}
|
|
|
|
|
2019-12-26 04:16:52 +01:00
|
|
|
void RasterizerOpenGL::SetupVertexBuffer() {
|
2019-03-09 07:25:11 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
2019-12-29 05:28:53 +01:00
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::VertexBuffers]) {
|
2018-11-06 21:26:27 +01:00
|
|
|
return;
|
2019-12-29 05:28:53 +01:00
|
|
|
}
|
|
|
|
flags[Dirty::VertexBuffers] = false;
|
2018-11-06 21:26:27 +01:00
|
|
|
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_VB);
|
|
|
|
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
const bool use_unified_memory = device.HasVertexBufferUnifiedMemory();
|
|
|
|
|
2018-04-22 02:19:33 +02:00
|
|
|
// Upload all guest vertex arrays sequentially to our buffer
|
2019-12-29 05:28:53 +01:00
|
|
|
const auto& regs = gpu.regs;
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) {
|
2019-12-29 05:28:53 +01:00
|
|
|
if (!flags[Dirty::VertexBuffer0 + index]) {
|
2018-11-06 21:26:27 +01:00
|
|
|
continue;
|
2019-12-29 05:28:53 +01:00
|
|
|
}
|
|
|
|
flags[Dirty::VertexBuffer0 + index] = false;
|
2018-11-06 21:26:27 +01:00
|
|
|
|
2018-04-22 02:19:33 +02:00
|
|
|
const auto& vertex_array = regs.vertex_array[index];
|
2019-12-26 04:16:52 +01:00
|
|
|
if (!vertex_array.IsEnabled()) {
|
2018-04-22 02:19:33 +02:00
|
|
|
continue;
|
2019-12-26 04:16:52 +01:00
|
|
|
}
|
2018-04-22 02:19:33 +02:00
|
|
|
|
2019-03-04 05:17:35 +01:00
|
|
|
const GPUVAddr start = vertex_array.StartAddress();
|
|
|
|
const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
|
2020-04-19 06:25:52 +02:00
|
|
|
ASSERT(end >= start);
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
|
|
|
|
const GLuint gl_index = static_cast<GLuint>(index);
|
2020-04-19 06:25:52 +02:00
|
|
|
const u64 size = end - start;
|
|
|
|
if (size == 0) {
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride);
|
|
|
|
if (use_unified_memory) {
|
|
|
|
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, 0, 0);
|
|
|
|
}
|
2020-04-19 06:25:52 +02:00
|
|
|
continue;
|
|
|
|
}
|
2020-05-11 21:35:04 +02:00
|
|
|
const auto info = buffer_cache.UploadMemory(start, size);
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
if (use_unified_memory) {
|
|
|
|
glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride);
|
|
|
|
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index,
|
|
|
|
info.address + info.offset, size);
|
|
|
|
} else {
|
|
|
|
glBindVertexBuffer(gl_index, info.handle, info.offset, vertex_array.stride);
|
|
|
|
}
|
2018-03-25 03:29:47 +02:00
|
|
|
}
|
2019-07-10 21:38:31 +02:00
|
|
|
}
|
2018-11-06 19:37:10 +01:00
|
|
|
|
2019-12-26 04:16:52 +01:00
|
|
|
void RasterizerOpenGL::SetupVertexInstances() {
|
2019-07-10 21:38:31 +02:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
2019-12-29 05:28:53 +01:00
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::VertexInstances]) {
|
2019-07-10 21:38:31 +02:00
|
|
|
return;
|
2019-12-29 05:28:53 +01:00
|
|
|
}
|
|
|
|
flags[Dirty::VertexInstances] = false;
|
2019-07-10 21:38:31 +02:00
|
|
|
|
|
|
|
const auto& regs = gpu.regs;
|
gl_rasterizer: Use NV_vertex_buffer_unified_memory for vertex buffer robustness
Switch games are allowed to bind less data than what they use in a
vertex buffer, the expected behavior here is that these values are read
as zero. At the moment of writing this only D3D12, OpenGL and NVN through
NV_vertex_buffer_unified_memory support vertex buffer with a size limit.
In theory this could be emulated on Vulkan creating a new VkBuffer for
each (handle, offset, length) tuple and binding the expected data to it.
This is likely going to be slow and memory expensive when used on the
vertex buffer and we have to do it on all draws because we can't know
without analyzing indices when a game is going to read vertex data out
of bounds.
This is not a problem on OpenGL's BufferAddressRangeNV because it takes
a length parameter, unlike Vulkan's CmdBindVertexBuffers that only takes
buffers and offsets (the length is implicit in VkBuffer). It isn't a
problem on D3D12 either, because D3D12_VERTEX_BUFFER_VIEW on
IASetVertexBuffers takes SizeInBytes as a parameter (although I am not
familiar with robustness on D3D12).
Currently this only implements buffer ranges for vertex buffers,
although indices can also be affected. A KHR_robustness profile is not
created, but Nvidia's driver reads out of bound vertex data as zero
anyway, this might have to be changed in the future.
- Fixes SMO random triangles when capturing an enemy, getting hit, or
looking at the environment on certain maps.
2020-06-18 08:56:31 +02:00
|
|
|
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
|
2019-12-29 05:28:53 +01:00
|
|
|
if (!flags[Dirty::VertexInstance0 + index]) {
|
2019-07-10 21:38:31 +02:00
|
|
|
continue;
|
|
|
|
}
|
2019-12-29 05:28:53 +01:00
|
|
|
flags[Dirty::VertexInstance0 + index] = false;
|
|
|
|
|
|
|
|
const auto gl_index = static_cast<GLuint>(index);
|
|
|
|
const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index);
|
|
|
|
const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0;
|
|
|
|
glVertexBindingDivisor(gl_index, divisor);
|
2019-07-10 21:38:31 +02:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
2019-06-20 08:44:06 +02:00
|
|
|
GLintptr RasterizerOpenGL::SetupIndexBuffer() {
|
2019-05-28 00:55:44 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_Index);
|
2019-05-28 00:37:46 +02:00
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
2019-05-28 00:55:44 +02:00
|
|
|
const std::size_t size = CalculateIndexBufferSize();
|
2020-05-11 21:35:04 +02:00
|
|
|
const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle);
|
|
|
|
return info.offset;
|
2019-05-28 00:37:46 +02:00
|
|
|
}
|
|
|
|
|
2018-10-07 04:17:31 +02:00
|
|
|
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
2018-09-04 11:02:59 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_Shader);
|
2019-03-09 07:25:11 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
2020-05-18 03:32:49 +02:00
|
|
|
std::size_t num_ssbos = 0;
|
2019-12-29 06:03:05 +01:00
|
|
|
u32 clip_distances = 0;
|
2018-04-15 21:14:57 +02:00
|
|
|
|
2018-09-15 15:21:06 +02:00
|
|
|
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
2018-09-08 08:59:59 +02:00
|
|
|
const auto& shader_config = gpu.regs.shader_config[index];
|
2019-11-18 22:35:21 +01:00
|
|
|
const auto program{static_cast<Maxwell::ShaderProgram>(index)};
|
2018-04-08 06:00:11 +02:00
|
|
|
|
|
|
|
// Skip stages that are not enabled
|
2018-07-13 04:57:57 +02:00
|
|
|
if (!gpu.regs.IsShaderConfigEnabled(index)) {
|
2018-10-07 04:17:31 +02:00
|
|
|
switch (program) {
|
|
|
|
case Maxwell::ShaderProgram::Geometry:
|
2019-12-26 20:04:41 +01:00
|
|
|
program_manager.UseGeometryShader(0);
|
2018-10-07 04:17:31 +02:00
|
|
|
break;
|
2019-12-26 20:38:39 +01:00
|
|
|
case Maxwell::ShaderProgram::Fragment:
|
2019-12-26 20:04:41 +01:00
|
|
|
program_manager.UseFragmentShader(0);
|
2019-12-26 20:38:39 +01:00
|
|
|
break;
|
2019-04-03 09:33:36 +02:00
|
|
|
default:
|
|
|
|
break;
|
2018-10-07 04:17:31 +02:00
|
|
|
}
|
2018-04-08 06:00:11 +02:00
|
|
|
continue;
|
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2019-12-11 20:41:26 +01:00
|
|
|
// Currently this stages are not supported in the OpenGL backend.
|
|
|
|
// Todo(Blinkhawk): Port tesselation shaders from Vulkan to OpenGL
|
|
|
|
if (program == Maxwell::ShaderProgram::TesselationControl) {
|
|
|
|
continue;
|
|
|
|
} else if (program == Maxwell::ShaderProgram::TesselationEval) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
Shader* const shader = shader_cache.GetStageProgram(program);
|
2019-04-29 02:08:31 +02:00
|
|
|
|
2020-05-18 03:32:49 +02:00
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
// Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this
|
|
|
|
// all stages share the same bindings.
|
|
|
|
const std::size_t num_stage_ssbos = shader->GetEntries().global_memory_entries.size();
|
|
|
|
ASSERT_MSG(num_stage_ssbos == 0 || num_ssbos == 0, "SSBOs on more than one stage");
|
|
|
|
num_ssbos += num_stage_ssbos;
|
|
|
|
}
|
|
|
|
|
2019-10-30 04:23:09 +01:00
|
|
|
// Stage indices are 0 - 5
|
2019-11-18 22:35:21 +01:00
|
|
|
const std::size_t stage = index == 0 ? 0 : index - 1;
|
2019-10-30 04:23:09 +01:00
|
|
|
SetupDrawConstBuffers(stage, shader);
|
|
|
|
SetupDrawGlobalMemory(stage, shader);
|
2019-11-19 01:38:15 +01:00
|
|
|
SetupDrawTextures(stage, shader);
|
|
|
|
SetupDrawImages(stage, shader);
|
2019-04-29 02:08:31 +02:00
|
|
|
|
2020-02-26 20:13:47 +01:00
|
|
|
const GLuint program_handle = shader->GetHandle();
|
2018-04-08 06:00:11 +02:00
|
|
|
switch (program) {
|
2018-08-23 23:30:27 +02:00
|
|
|
case Maxwell::ShaderProgram::VertexA:
|
2019-01-05 05:00:06 +01:00
|
|
|
case Maxwell::ShaderProgram::VertexB:
|
2019-12-26 20:04:41 +01:00
|
|
|
program_manager.UseVertexShader(program_handle);
|
2018-10-07 04:17:31 +02:00
|
|
|
break;
|
2019-01-05 05:00:06 +01:00
|
|
|
case Maxwell::ShaderProgram::Geometry:
|
2019-12-26 20:04:41 +01:00
|
|
|
program_manager.UseGeometryShader(program_handle);
|
2018-04-08 06:00:11 +02:00
|
|
|
break;
|
2019-01-05 05:00:06 +01:00
|
|
|
case Maxwell::ShaderProgram::Fragment:
|
2019-12-26 20:04:41 +01:00
|
|
|
program_manager.UseFragmentShader(program_handle);
|
2018-04-08 06:00:11 +02:00
|
|
|
break;
|
|
|
|
default:
|
2019-03-09 07:33:56 +01:00
|
|
|
UNIMPLEMENTED_MSG("Unimplemented shader index={}, enable={}, offset=0x{:08X}", index,
|
|
|
|
shader_config.enable.Value(), shader_config.offset);
|
2018-04-08 06:00:11 +02:00
|
|
|
}
|
2018-04-15 18:15:54 +02:00
|
|
|
|
2018-11-29 20:13:13 +01:00
|
|
|
// Workaround for Intel drivers.
|
|
|
|
// When a clip distance is enabled but not set in the shader it crops parts of the screen
|
|
|
|
// (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
|
|
|
|
// clip distances only when it's written by a shader stage.
|
2020-02-26 20:13:47 +01:00
|
|
|
clip_distances |= shader->GetEntries().clip_distances;
|
2018-11-29 20:13:13 +01:00
|
|
|
|
2018-07-13 04:25:03 +02:00
|
|
|
// When VertexA is enabled, we have dual vertex shaders
|
|
|
|
if (program == Maxwell::ShaderProgram::VertexA) {
|
|
|
|
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
2019-11-06 08:32:43 +01:00
|
|
|
++index;
|
2018-07-13 04:25:03 +02:00
|
|
|
}
|
2018-04-08 06:00:11 +02:00
|
|
|
}
|
2018-11-29 20:13:13 +01:00
|
|
|
|
|
|
|
SyncClipEnabled(clip_distances);
|
2019-12-29 06:03:05 +01:00
|
|
|
gpu.dirty.flags[Dirty::Shaders] = false;
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
2018-09-15 15:21:06 +02:00
|
|
|
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
|
2019-03-09 07:25:11 +01:00
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
2018-04-22 02:19:33 +02:00
|
|
|
|
2018-09-15 15:21:06 +02:00
|
|
|
std::size_t size = 0;
|
2018-04-22 02:19:33 +02:00
|
|
|
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
|
|
|
if (!regs.vertex_array[index].IsEnabled())
|
|
|
|
continue;
|
|
|
|
|
2019-03-04 05:17:35 +01:00
|
|
|
const GPUVAddr start = regs.vertex_array[index].StartAddress();
|
|
|
|
const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
|
2018-04-22 02:19:33 +02:00
|
|
|
|
2020-04-19 06:25:52 +02:00
|
|
|
size += end - start;
|
|
|
|
ASSERT(end >= start);
|
2018-04-22 02:19:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2018-10-02 19:47:26 +02:00
|
|
|
std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
|
2019-03-09 07:25:11 +01:00
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
2018-10-02 19:47:26 +02:00
|
|
|
|
|
|
|
return static_cast<std::size_t>(regs.index_array.count) *
|
|
|
|
static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
|
|
|
|
}
|
|
|
|
|
2019-01-21 20:38:23 +01:00
|
|
|
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
|
|
|
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
|
|
|
shader_cache.LoadDiskCache(stop_loading, callback);
|
2019-01-14 02:05:53 +01:00
|
|
|
}
|
|
|
|
|
2019-12-29 01:45:56 +01:00
|
|
|
void RasterizerOpenGL::SetupDirtyFlags() {
|
|
|
|
state_tracker.Initialize();
|
|
|
|
}
|
|
|
|
|
2019-09-17 07:36:47 +02:00
|
|
|
void RasterizerOpenGL::ConfigureFramebuffers() {
|
2018-09-04 11:02:59 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
2019-03-09 07:25:11 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
2019-12-29 01:45:56 +01:00
|
|
|
if (!gpu.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
|
2019-09-17 07:36:47 +02:00
|
|
|
return;
|
2019-01-07 06:22:00 +01:00
|
|
|
}
|
2019-12-29 01:45:56 +01:00
|
|
|
gpu.dirty.flags[VideoCommon::Dirty::RenderTargets] = false;
|
2018-03-24 08:59:51 +01:00
|
|
|
|
2019-06-15 19:22:57 +02:00
|
|
|
texture_cache.GuardRenderTargets(true);
|
2019-05-14 01:14:02 +02:00
|
|
|
|
2020-04-27 00:53:02 +02:00
|
|
|
View depth_surface = texture_cache.GetDepthBufferSurface(true);
|
2018-03-24 08:59:51 +01:00
|
|
|
|
2019-09-17 07:36:47 +02:00
|
|
|
const auto& regs = gpu.regs;
|
2019-01-22 08:14:29 +01:00
|
|
|
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
|
2018-03-24 08:59:51 +01:00
|
|
|
|
|
|
|
// Bind the framebuffer surfaces
|
2019-11-29 02:59:09 +01:00
|
|
|
FramebufferCacheKey key;
|
|
|
|
const auto colors_count = static_cast<std::size_t>(regs.rt_control.count);
|
|
|
|
for (std::size_t index = 0; index < colors_count; ++index) {
|
2020-04-27 00:53:02 +02:00
|
|
|
View color_surface{texture_cache.GetColorBufferSurface(index, true)};
|
2019-11-29 02:59:09 +01:00
|
|
|
if (!color_surface) {
|
|
|
|
continue;
|
2018-09-10 06:36:13 +02:00
|
|
|
}
|
2019-11-29 02:59:09 +01:00
|
|
|
// Assume that a surface will be written to if it is used as a framebuffer, even
|
|
|
|
// if the shader doesn't actually write to it.
|
|
|
|
texture_cache.MarkColorBufferInUse(index);
|
2019-09-17 07:36:47 +02:00
|
|
|
|
2019-11-29 02:59:09 +01:00
|
|
|
key.SetAttachment(index, regs.rt_control.GetMap(index));
|
|
|
|
key.colors[index] = std::move(color_surface);
|
2018-09-10 01:01:21 +02:00
|
|
|
}
|
2018-03-27 04:54:16 +02:00
|
|
|
|
2018-09-10 01:01:21 +02:00
|
|
|
if (depth_surface) {
|
2018-10-13 04:31:04 +02:00
|
|
|
// Assume that a surface will be written to if it is used as a framebuffer, even if
|
|
|
|
// the shader doesn't actually write to it.
|
2019-05-07 16:57:16 +02:00
|
|
|
texture_cache.MarkDepthBufferInUse();
|
2019-11-29 02:59:09 +01:00
|
|
|
key.zeta = std::move(depth_surface);
|
2018-09-10 01:01:21 +02:00
|
|
|
}
|
2018-11-19 02:20:26 +01:00
|
|
|
|
2019-06-15 19:22:57 +02:00
|
|
|
texture_cache.GuardRenderTargets(false);
|
2019-05-14 01:14:02 +02:00
|
|
|
|
2019-12-26 09:01:43 +01:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
|
2018-07-03 23:55:44 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 00:53:02 +02:00
|
|
|
void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil) {
|
2019-07-14 14:14:27 +02:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
const auto& regs = gpu.regs;
|
|
|
|
|
|
|
|
texture_cache.GuardRenderTargets(true);
|
2019-11-29 01:04:57 +01:00
|
|
|
View color_surface;
|
2020-04-27 00:53:02 +02:00
|
|
|
|
|
|
|
if (using_color) {
|
|
|
|
// Determine if we have to preserve the contents.
|
|
|
|
// First we have to make sure all clear masks are enabled.
|
|
|
|
bool preserve_contents = !regs.clear_buffers.R || !regs.clear_buffers.G ||
|
|
|
|
!regs.clear_buffers.B || !regs.clear_buffers.A;
|
2020-03-31 10:51:56 +02:00
|
|
|
const std::size_t index = regs.clear_buffers.RT;
|
2020-04-27 00:53:02 +02:00
|
|
|
if (regs.clear_flags.scissor) {
|
|
|
|
// Then we have to confirm scissor testing clears the whole image.
|
|
|
|
const auto& scissor = regs.scissor_test[0];
|
|
|
|
preserve_contents |= scissor.min_x > 0;
|
|
|
|
preserve_contents |= scissor.min_y > 0;
|
|
|
|
preserve_contents |= scissor.max_x < regs.rt[index].width;
|
|
|
|
preserve_contents |= scissor.max_y < regs.rt[index].height;
|
|
|
|
}
|
|
|
|
|
|
|
|
color_surface = texture_cache.GetColorBufferSurface(index, preserve_contents);
|
2020-03-31 10:51:56 +02:00
|
|
|
texture_cache.MarkColorBufferInUse(index);
|
2019-07-14 14:14:27 +02:00
|
|
|
}
|
2020-04-27 00:53:02 +02:00
|
|
|
|
2019-11-29 01:04:57 +01:00
|
|
|
View depth_surface;
|
2020-04-27 00:53:02 +02:00
|
|
|
if (using_depth_stencil) {
|
|
|
|
bool preserve_contents = false;
|
|
|
|
if (regs.clear_flags.scissor) {
|
|
|
|
// For depth stencil clears we only have to confirm scissor test covers the whole image.
|
|
|
|
const auto& scissor = regs.scissor_test[0];
|
|
|
|
preserve_contents |= scissor.min_x > 0;
|
|
|
|
preserve_contents |= scissor.min_y > 0;
|
|
|
|
preserve_contents |= scissor.max_x < regs.zeta_width;
|
|
|
|
preserve_contents |= scissor.max_y < regs.zeta_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
depth_surface = texture_cache.GetDepthBufferSurface(preserve_contents);
|
2020-03-31 10:51:56 +02:00
|
|
|
texture_cache.MarkDepthBufferInUse();
|
2019-07-14 14:14:27 +02:00
|
|
|
}
|
|
|
|
texture_cache.GuardRenderTargets(false);
|
|
|
|
|
2019-11-29 01:04:57 +01:00
|
|
|
FramebufferCacheKey key;
|
2020-04-27 00:53:02 +02:00
|
|
|
key.colors[0] = std::move(color_surface);
|
|
|
|
key.zeta = std::move(depth_surface);
|
2019-07-14 14:14:27 +02:00
|
|
|
|
2019-12-29 01:45:56 +01:00
|
|
|
state_tracker.NotifyFramebuffer();
|
2019-12-26 09:01:43 +01:00
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
|
2019-07-14 14:14:27 +02:00
|
|
|
}
|
|
|
|
|
2018-07-03 23:55:44 +02:00
|
|
|
void RasterizerOpenGL::Clear() {
|
2020-01-03 02:30:41 +01:00
|
|
|
const auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
if (!gpu.ShouldExecute()) {
|
2019-07-01 04:21:28 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-03 02:30:41 +01:00
|
|
|
const auto& regs = gpu.regs;
|
2018-09-10 06:36:13 +02:00
|
|
|
bool use_color{};
|
|
|
|
bool use_depth{};
|
|
|
|
bool use_stencil{};
|
2018-07-04 05:32:59 +02:00
|
|
|
|
2018-08-22 06:33:03 +02:00
|
|
|
if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
|
2018-07-03 23:55:44 +02:00
|
|
|
regs.clear_buffers.A) {
|
2018-09-10 06:36:13 +02:00
|
|
|
use_color = true;
|
2020-04-27 00:53:02 +02:00
|
|
|
|
2019-12-29 02:51:04 +01:00
|
|
|
state_tracker.NotifyColorMask0();
|
|
|
|
glColorMaski(0, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0,
|
|
|
|
regs.clear_buffers.B != 0, regs.clear_buffers.A != 0);
|
2019-12-26 05:19:15 +01:00
|
|
|
|
2019-12-26 05:07:34 +01:00
|
|
|
// TODO(Rodrigo): Determine if clamping is used on clears
|
|
|
|
SyncFragmentColorClampState();
|
2020-01-03 02:30:41 +01:00
|
|
|
SyncFramebufferSRGB();
|
2018-11-08 02:27:47 +01:00
|
|
|
}
|
2018-07-04 05:32:59 +02:00
|
|
|
if (regs.clear_buffers.Z) {
|
2018-08-22 06:33:03 +02:00
|
|
|
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
|
2018-09-10 06:36:13 +02:00
|
|
|
use_depth = true;
|
2018-07-14 07:52:23 +02:00
|
|
|
|
2020-01-03 02:31:04 +01:00
|
|
|
state_tracker.NotifyDepthMask();
|
2019-12-26 01:52:39 +01:00
|
|
|
glDepthMask(GL_TRUE);
|
2018-08-22 06:33:03 +02:00
|
|
|
}
|
|
|
|
if (regs.clear_buffers.S) {
|
2020-01-03 02:30:41 +01:00
|
|
|
ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
|
2018-09-10 06:36:13 +02:00
|
|
|
use_stencil = true;
|
2018-07-04 05:32:59 +02:00
|
|
|
}
|
2018-07-03 23:55:44 +02:00
|
|
|
|
2018-09-10 06:36:13 +02:00
|
|
|
if (!use_color && !use_depth && !use_stencil) {
|
2018-08-22 06:33:03 +02:00
|
|
|
// No color surface nor depth/stencil surface are enabled
|
2018-07-03 23:55:44 +02:00
|
|
|
return;
|
2018-08-22 06:33:03 +02:00
|
|
|
}
|
|
|
|
|
2019-12-26 06:11:01 +01:00
|
|
|
SyncRasterizeEnable();
|
2020-03-26 04:51:47 +01:00
|
|
|
SyncStencilTestState();
|
2019-07-14 21:00:37 +02:00
|
|
|
|
2018-11-21 04:40:32 +01:00
|
|
|
if (regs.clear_flags.scissor) {
|
2019-12-26 05:28:17 +01:00
|
|
|
SyncScissorTest();
|
2020-01-03 02:31:33 +01:00
|
|
|
} else {
|
|
|
|
state_tracker.NotifyScissor0();
|
|
|
|
glDisablei(GL_SCISSOR_TEST, 0);
|
2018-11-21 04:40:32 +01:00
|
|
|
}
|
|
|
|
|
2019-12-26 05:28:17 +01:00
|
|
|
UNIMPLEMENTED_IF(regs.clear_flags.viewport);
|
2018-11-21 04:40:32 +01:00
|
|
|
|
2020-04-27 00:53:02 +02:00
|
|
|
ConfigureClearFramebuffer(use_color, use_depth || use_stencil);
|
2018-08-22 06:33:03 +02:00
|
|
|
|
2018-09-10 06:36:13 +02:00
|
|
|
if (use_color) {
|
2019-07-14 14:14:27 +02:00
|
|
|
glClearBufferfv(GL_COLOR, 0, regs.clear_color);
|
2018-09-10 06:36:13 +02:00
|
|
|
}
|
2018-06-07 06:54:25 +02:00
|
|
|
|
2019-07-14 14:14:27 +02:00
|
|
|
if (use_depth && use_stencil) {
|
2018-09-10 06:36:13 +02:00
|
|
|
glClearBufferfi(GL_DEPTH_STENCIL, 0, regs.clear_depth, regs.clear_stencil);
|
2019-07-14 14:14:27 +02:00
|
|
|
} else if (use_depth) {
|
2018-09-10 06:36:13 +02:00
|
|
|
glClearBufferfv(GL_DEPTH, 0, ®s.clear_depth);
|
2019-07-14 14:14:27 +02:00
|
|
|
} else if (use_stencil) {
|
2018-09-10 06:36:13 +02:00
|
|
|
glClearBufferiv(GL_STENCIL, 0, ®s.clear_stencil);
|
|
|
|
}
|
2019-11-26 22:30:21 +01:00
|
|
|
|
|
|
|
++num_queued_commands;
|
2018-06-07 06:54:25 +02:00
|
|
|
}
|
|
|
|
|
2020-01-30 06:08:46 +01:00
|
|
|
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
2019-03-09 07:25:11 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
2019-11-26 22:52:15 +01:00
|
|
|
|
|
|
|
query_cache.UpdateCounters();
|
2019-07-28 00:40:10 +02:00
|
|
|
|
2019-12-26 05:50:38 +01:00
|
|
|
SyncViewport();
|
2019-12-26 06:11:01 +01:00
|
|
|
SyncRasterizeEnable();
|
2020-02-24 23:43:57 +01:00
|
|
|
SyncPolygonModes();
|
2018-11-05 03:46:06 +01:00
|
|
|
SyncColorMask();
|
2018-11-14 02:09:01 +01:00
|
|
|
SyncFragmentColorClampState();
|
2018-11-14 04:02:54 +01:00
|
|
|
SyncMultiSampleState();
|
2018-07-14 07:52:23 +02:00
|
|
|
SyncDepthTestState();
|
2020-01-03 02:41:20 +01:00
|
|
|
SyncDepthClamp();
|
2018-08-22 06:35:31 +02:00
|
|
|
SyncStencilTestState();
|
2018-06-09 00:05:52 +02:00
|
|
|
SyncBlendState();
|
2018-08-21 01:44:47 +02:00
|
|
|
SyncLogicOpState();
|
2018-07-02 20:33:41 +02:00
|
|
|
SyncCullMode();
|
2018-10-26 01:04:13 +02:00
|
|
|
SyncPrimitiveRestart();
|
2019-12-26 05:28:17 +01:00
|
|
|
SyncScissorTest();
|
2018-09-28 06:31:01 +02:00
|
|
|
SyncPointState();
|
2020-02-25 01:02:32 +01:00
|
|
|
SyncLineState();
|
2018-11-27 00:31:44 +01:00
|
|
|
SyncPolygonOffset();
|
2019-05-22 01:28:09 +02:00
|
|
|
SyncAlphaTest();
|
2019-12-26 05:01:41 +01:00
|
|
|
SyncFramebufferSRGB();
|
2018-03-27 04:54:16 +02:00
|
|
|
|
2019-11-02 08:08:31 +01:00
|
|
|
buffer_cache.Acquire();
|
2020-05-18 03:32:49 +02:00
|
|
|
current_cbuf = 0;
|
2019-11-02 08:08:31 +01:00
|
|
|
|
2018-09-15 15:21:06 +02:00
|
|
|
std::size_t buffer_size = CalculateVertexArraysSize();
|
2018-04-22 02:19:33 +02:00
|
|
|
|
2019-05-22 00:21:57 +02:00
|
|
|
// Add space for index buffer
|
|
|
|
if (is_indexed) {
|
|
|
|
buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize();
|
2018-03-24 08:59:51 +01:00
|
|
|
}
|
2018-04-08 06:00:11 +02:00
|
|
|
|
|
|
|
// Uniform space for the 5 shader stages
|
2020-05-18 03:32:49 +02:00
|
|
|
buffer_size =
|
|
|
|
Common::AlignUp<std::size_t>(buffer_size, 4) +
|
|
|
|
(sizeof(MaxwellUniformData) + device.GetUniformBufferAlignment()) * Maxwell::MaxShaderStage;
|
2018-03-24 08:59:51 +01:00
|
|
|
|
2018-08-10 10:29:37 +02:00
|
|
|
// Add space for at least 18 constant buffers
|
2019-07-06 04:11:58 +02:00
|
|
|
buffer_size += Maxwell::MaxConstBuffers *
|
|
|
|
(Maxwell::MaxConstBufferSize + device.GetUniformBufferAlignment());
|
2018-08-10 10:29:37 +02:00
|
|
|
|
2019-06-20 08:22:25 +02:00
|
|
|
// Prepare the vertex array.
|
2020-06-09 01:24:16 +02:00
|
|
|
const bool invalidated = buffer_cache.Map(buffer_size);
|
|
|
|
|
|
|
|
if (invalidated) {
|
|
|
|
// When the stream buffer has been invalidated, we have to consider vertex buffers as dirty
|
|
|
|
auto& dirty = gpu.dirty.flags;
|
|
|
|
dirty[Dirty::VertexBuffers] = true;
|
|
|
|
for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) {
|
|
|
|
dirty[index] = true;
|
|
|
|
}
|
|
|
|
}
|
2018-03-24 08:59:51 +01:00
|
|
|
|
2019-05-28 00:41:19 +02:00
|
|
|
// Prepare vertex array format.
|
2019-12-26 04:16:52 +01:00
|
|
|
SetupVertexFormat();
|
2019-05-28 00:37:46 +02:00
|
|
|
|
2019-05-28 00:41:19 +02:00
|
|
|
// Upload vertex and index data.
|
2019-12-26 04:16:52 +01:00
|
|
|
SetupVertexBuffer();
|
|
|
|
SetupVertexInstances();
|
2020-03-18 21:25:14 +01:00
|
|
|
GLintptr index_buffer_offset = 0;
|
2020-01-30 06:08:46 +01:00
|
|
|
if (is_indexed) {
|
|
|
|
index_buffer_offset = SetupIndexBuffer();
|
|
|
|
}
|
2019-05-28 00:41:19 +02:00
|
|
|
|
2019-11-19 01:38:15 +01:00
|
|
|
// Setup emulation uniform buffer.
|
2020-05-18 03:32:49 +02:00
|
|
|
if (!device.UseAssemblyShaders()) {
|
|
|
|
MaxwellUniformData ubo;
|
|
|
|
ubo.SetFromRegs(gpu);
|
2020-05-11 21:35:04 +02:00
|
|
|
const auto info =
|
2020-05-18 03:32:49 +02:00
|
|
|
buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
|
2020-05-11 21:35:04 +02:00
|
|
|
glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset,
|
2020-05-18 03:32:49 +02:00
|
|
|
static_cast<GLsizeiptr>(sizeof(ubo)));
|
|
|
|
}
|
2019-06-20 08:22:25 +02:00
|
|
|
|
2019-05-28 00:41:19 +02:00
|
|
|
// Setup shaders and their used resources.
|
2019-06-15 19:22:57 +02:00
|
|
|
texture_cache.GuardSamplers(true);
|
2020-02-14 01:55:21 +01:00
|
|
|
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
|
2019-09-15 17:48:54 +02:00
|
|
|
SetupShaders(primitive_mode);
|
2019-06-15 19:22:57 +02:00
|
|
|
texture_cache.GuardSamplers(false);
|
2018-03-24 08:59:51 +01:00
|
|
|
|
2019-09-17 07:36:47 +02:00
|
|
|
ConfigureFramebuffers();
|
2019-05-08 23:45:59 +02:00
|
|
|
|
2019-06-20 08:22:25 +02:00
|
|
|
// Signal the buffer cache that we are not going to upload more things.
|
2020-03-18 21:25:14 +01:00
|
|
|
buffer_cache.Unmap();
|
2019-06-20 08:22:25 +02:00
|
|
|
|
2020-03-11 05:03:01 +01:00
|
|
|
program_manager.BindGraphicsPipeline();
|
2018-03-24 08:59:51 +01:00
|
|
|
|
2019-06-15 19:22:57 +02:00
|
|
|
if (texture_cache.TextureBarrier()) {
|
|
|
|
glTextureBarrier();
|
|
|
|
}
|
2019-09-20 21:44:28 +02:00
|
|
|
|
2020-03-02 23:31:26 +01:00
|
|
|
BeginTransformFeedback(primitive_mode);
|
2019-11-26 22:30:21 +01:00
|
|
|
|
2020-01-30 06:08:46 +01:00
|
|
|
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
|
|
|
|
const GLsizei num_instances =
|
|
|
|
static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1);
|
|
|
|
if (is_indexed) {
|
|
|
|
const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base);
|
|
|
|
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count);
|
2020-02-14 01:55:21 +01:00
|
|
|
const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset);
|
|
|
|
const GLenum format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
|
|
|
|
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
|
|
|
|
glDrawElements(primitive_mode, num_vertices, format, offset);
|
|
|
|
} else if (num_instances == 1 && base_instance == 0) {
|
|
|
|
glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex);
|
|
|
|
} else if (base_vertex == 0 && base_instance == 0) {
|
|
|
|
glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances);
|
|
|
|
} else if (base_vertex == 0) {
|
|
|
|
glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset,
|
|
|
|
num_instances, base_instance);
|
|
|
|
} else if (base_instance == 0) {
|
|
|
|
glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset,
|
|
|
|
num_instances, base_vertex);
|
|
|
|
} else {
|
|
|
|
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format,
|
|
|
|
offset, num_instances, base_vertex,
|
|
|
|
base_instance);
|
|
|
|
}
|
2019-09-15 17:48:54 +02:00
|
|
|
} else {
|
2020-01-30 06:08:46 +01:00
|
|
|
const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first);
|
|
|
|
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count);
|
2020-02-14 01:55:21 +01:00
|
|
|
if (num_instances == 1 && base_instance == 0) {
|
|
|
|
glDrawArrays(primitive_mode, base_vertex, num_vertices);
|
|
|
|
} else if (base_instance == 0) {
|
|
|
|
glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances);
|
|
|
|
} else {
|
|
|
|
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices,
|
|
|
|
num_instances, base_instance);
|
|
|
|
}
|
2019-09-15 17:48:54 +02:00
|
|
|
}
|
2020-03-02 23:31:26 +01:00
|
|
|
|
|
|
|
EndTransformFeedback();
|
|
|
|
|
|
|
|
++num_queued_commands;
|
2020-02-20 16:55:32 +01:00
|
|
|
|
|
|
|
system.GPU().TickWork();
|
2020-01-30 06:08:46 +01:00
|
|
|
}
|
2019-06-15 19:22:57 +02:00
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
|
2019-11-21 18:21:27 +01:00
|
|
|
buffer_cache.Acquire();
|
2020-05-18 03:32:49 +02:00
|
|
|
current_cbuf = 0;
|
2019-11-21 18:21:27 +01:00
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
auto kernel = shader_cache.GetComputeKernel(code_addr);
|
2019-11-06 08:32:43 +01:00
|
|
|
SetupComputeTextures(kernel);
|
2019-07-12 02:54:07 +02:00
|
|
|
SetupComputeImages(kernel);
|
2019-07-15 03:25:13 +02:00
|
|
|
|
|
|
|
const std::size_t buffer_size =
|
|
|
|
Tegra::Engines::KeplerCompute::NumConstBuffers *
|
|
|
|
(Maxwell::MaxConstBufferSize + device.GetUniformBufferAlignment());
|
|
|
|
buffer_cache.Map(buffer_size);
|
|
|
|
|
|
|
|
SetupComputeConstBuffers(kernel);
|
|
|
|
SetupComputeGlobalMemory(kernel);
|
|
|
|
|
|
|
|
buffer_cache.Unmap();
|
|
|
|
|
2020-02-26 20:13:47 +01:00
|
|
|
const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
|
2020-05-18 03:32:49 +02:00
|
|
|
program_manager.BindCompute(kernel->GetHandle());
|
2019-11-13 03:26:56 +01:00
|
|
|
glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
|
2019-11-26 22:30:21 +01:00
|
|
|
++num_queued_commands;
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
|
2019-07-28 00:40:10 +02:00
|
|
|
void RasterizerOpenGL::ResetCounter(VideoCore::QueryType type) {
|
2019-11-26 22:52:15 +01:00
|
|
|
query_cache.ResetCounter(type);
|
2019-07-28 00:40:10 +02:00
|
|
|
}
|
|
|
|
|
2019-11-28 06:15:34 +01:00
|
|
|
void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
|
|
|
|
std::optional<u64> timestamp) {
|
|
|
|
query_cache.Query(gpu_addr, type, timestamp);
|
2019-07-28 00:40:10 +02:00
|
|
|
}
|
|
|
|
|
2018-09-08 10:05:56 +02:00
|
|
|
void RasterizerOpenGL::FlushAll() {}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2020-04-05 18:58:23 +02:00
|
|
|
void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
|
2018-10-13 04:31:04 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
2020-04-08 19:34:59 +02:00
|
|
|
if (addr == 0 || size == 0) {
|
2019-02-28 03:21:31 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-04-05 21:26:16 +02:00
|
|
|
texture_cache.FlushRegion(addr, size);
|
2020-04-05 23:23:49 +02:00
|
|
|
buffer_cache.FlushRegion(addr, size);
|
2020-04-06 00:39:24 +02:00
|
|
|
query_cache.FlushRegion(addr, size);
|
2018-10-13 04:31:04 +02:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2020-02-18 03:29:04 +01:00
|
|
|
bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
|
2020-04-19 19:47:45 +02:00
|
|
|
if (!Settings::IsGPULevelHigh()) {
|
|
|
|
return buffer_cache.MustFlushRegion(addr, size);
|
|
|
|
}
|
2020-02-18 03:29:04 +01:00
|
|
|
return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size);
|
|
|
|
}
|
|
|
|
|
2020-04-05 18:58:23 +02:00
|
|
|
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
|
2018-06-26 22:14:14 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
2020-04-08 19:34:59 +02:00
|
|
|
if (addr == 0 || size == 0) {
|
2019-02-28 03:21:31 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-04-05 21:26:16 +02:00
|
|
|
texture_cache.InvalidateRegion(addr, size);
|
2020-04-06 01:18:00 +02:00
|
|
|
shader_cache.InvalidateRegion(addr, size);
|
2020-04-05 23:23:49 +02:00
|
|
|
buffer_cache.InvalidateRegion(addr, size);
|
2020-04-06 00:39:24 +02:00
|
|
|
query_cache.InvalidateRegion(addr, size);
|
2018-06-26 22:14:14 +02:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2020-02-16 14:51:37 +01:00
|
|
|
void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
2020-04-16 18:29:53 +02:00
|
|
|
if (addr == 0 || size == 0) {
|
2020-02-16 14:51:37 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
texture_cache.OnCPUWrite(addr, size);
|
2020-05-10 01:25:29 +02:00
|
|
|
shader_cache.OnCPUWrite(addr, size);
|
2020-02-16 15:08:07 +01:00
|
|
|
buffer_cache.OnCPUWrite(addr, size);
|
2020-02-16 14:51:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncGuestHost() {
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
|
|
|
texture_cache.SyncGuestHost();
|
2020-02-16 15:08:07 +01:00
|
|
|
buffer_cache.SyncGuestHost();
|
2020-05-10 01:25:29 +02:00
|
|
|
shader_cache.SyncGuestHost();
|
2020-02-16 14:51:37 +01:00
|
|
|
}
|
|
|
|
|
2020-02-19 18:40:37 +01:00
|
|
|
void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
|
2020-02-18 16:26:31 +01:00
|
|
|
auto& gpu{system.GPU()};
|
|
|
|
if (!gpu.IsAsync()) {
|
|
|
|
auto& memory_manager{gpu.MemoryManager()};
|
|
|
|
memory_manager.Write<u32>(addr, value);
|
|
|
|
return;
|
|
|
|
}
|
2020-02-19 18:40:37 +01:00
|
|
|
fence_manager.SignalSemaphore(addr, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::SignalSyncPoint(u32 value) {
|
|
|
|
auto& gpu{system.GPU()};
|
|
|
|
if (!gpu.IsAsync()) {
|
|
|
|
gpu.IncrementSyncPoint(value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fence_manager.SignalSyncPoint(value);
|
2020-02-17 23:10:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::ReleaseFences() {
|
2020-02-18 16:26:31 +01:00
|
|
|
auto& gpu{system.GPU()};
|
|
|
|
if (!gpu.IsAsync()) {
|
|
|
|
return;
|
|
|
|
}
|
2020-02-18 01:19:26 +01:00
|
|
|
fence_manager.WaitPendingFences();
|
2020-02-17 23:10:23 +01:00
|
|
|
}
|
|
|
|
|
2020-04-05 18:58:23 +02:00
|
|
|
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
2020-02-18 21:51:42 +01:00
|
|
|
if (Settings::IsGPULevelExtreme()) {
|
2019-05-08 16:32:30 +02:00
|
|
|
FlushRegion(addr, size);
|
|
|
|
}
|
2018-08-23 21:44:41 +02:00
|
|
|
InvalidateRegion(addr, size);
|
2018-06-26 22:14:14 +02:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2020-04-28 07:14:11 +02:00
|
|
|
void RasterizerOpenGL::WaitForIdle() {
|
|
|
|
// Place a barrier on everything that is not framebuffer related.
|
|
|
|
// This is related to another flag that is not currently implemented.
|
|
|
|
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_ELEMENT_ARRAY_BARRIER_BIT |
|
|
|
|
GL_UNIFORM_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT |
|
|
|
|
GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_COMMAND_BARRIER_BIT |
|
|
|
|
GL_PIXEL_BUFFER_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT |
|
|
|
|
GL_BUFFER_UPDATE_BARRIER_BIT | GL_TRANSFORM_FEEDBACK_BARRIER_BIT |
|
|
|
|
GL_SHADER_STORAGE_BARRIER_BIT | GL_QUERY_BUFFER_BARRIER_BIT);
|
|
|
|
}
|
|
|
|
|
2019-07-26 20:20:43 +02:00
|
|
|
void RasterizerOpenGL::FlushCommands() {
|
2019-11-26 22:30:21 +01:00
|
|
|
// Only flush when we have commands queued to OpenGL.
|
|
|
|
if (num_queued_commands == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
num_queued_commands = 0;
|
2019-07-26 20:20:43 +02:00
|
|
|
glFlush();
|
|
|
|
}
|
|
|
|
|
2019-06-20 08:22:25 +02:00
|
|
|
void RasterizerOpenGL::TickFrame() {
|
2019-11-26 22:30:21 +01:00
|
|
|
// Ticking a frame means that buffers will be swapped, calling glFlush implicitly.
|
|
|
|
num_queued_commands = 0;
|
|
|
|
|
2019-06-20 08:22:25 +02:00
|
|
|
buffer_cache.TickFrame();
|
|
|
|
}
|
|
|
|
|
2018-10-06 05:39:03 +02:00
|
|
|
bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
|
2018-12-15 06:20:00 +01:00
|
|
|
const Tegra::Engines::Fermi2D::Regs::Surface& dst,
|
2019-05-18 10:57:49 +02:00
|
|
|
const Tegra::Engines::Fermi2D::Config& copy_config) {
|
2018-03-20 04:00:59 +01:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_Blits);
|
2019-05-18 10:57:49 +02:00
|
|
|
texture_cache.DoFermiCopy(src, dst, copy_config);
|
2018-03-20 04:00:59 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-24 23:42:29 +02:00
|
|
|
bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
2018-08-21 01:34:02 +02:00
|
|
|
VAddr framebuffer_addr, u32 pixel_stride) {
|
2018-06-24 23:42:29 +02:00
|
|
|
if (!framebuffer_addr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
|
|
|
|
2020-04-05 21:26:16 +02:00
|
|
|
const auto surface{texture_cache.TryFindFramebufferSurface(framebuffer_addr)};
|
2018-06-24 23:42:29 +02:00
|
|
|
if (!surface) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the cached surface is the same size and format as the requested framebuffer
|
|
|
|
const auto& params{surface->GetSurfaceParams()};
|
2018-10-29 02:14:25 +01:00
|
|
|
const auto& pixel_format{
|
|
|
|
VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)};
|
2019-05-07 16:57:16 +02:00
|
|
|
ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
|
|
|
|
ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
|
2019-03-09 07:33:56 +01:00
|
|
|
|
2019-05-07 16:57:16 +02:00
|
|
|
if (params.pixel_format != pixel_format) {
|
2019-11-19 01:38:15 +01:00
|
|
|
LOG_DEBUG(Render_OpenGL, "Framebuffer pixel_format is different");
|
2019-03-09 07:33:56 +01:00
|
|
|
}
|
2018-06-24 23:42:29 +02:00
|
|
|
|
2019-04-11 22:14:55 +02:00
|
|
|
screen_info.display_texture = surface->GetTexture();
|
2019-09-03 06:05:23 +02:00
|
|
|
screen_info.display_srgb = surface->GetSurfaceParams().srgb_conversion;
|
2018-06-24 23:42:29 +02:00
|
|
|
|
|
|
|
return true;
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* shader) {
|
2020-05-18 03:32:49 +02:00
|
|
|
static constexpr std::array PARAMETER_LUT = {
|
|
|
|
GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
|
|
|
|
GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV,
|
|
|
|
GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV};
|
|
|
|
|
2018-09-04 11:02:59 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_UBO);
|
2019-07-15 03:25:13 +02:00
|
|
|
const auto& stages = system.GPU().Maxwell3D().state.shader_stages;
|
2019-11-18 22:35:21 +01:00
|
|
|
const auto& shader_stage = stages[stage_index];
|
2020-05-28 22:06:22 +02:00
|
|
|
const auto& entries = shader->GetEntries();
|
|
|
|
const bool use_unified = entries.use_unified_uniforms;
|
|
|
|
const std::size_t base_unified_offset = stage_index * NUM_CONST_BUFFERS_BYTES_PER_STAGE;
|
2019-11-19 01:38:15 +01:00
|
|
|
|
2020-05-28 22:06:22 +02:00
|
|
|
const auto base_bindings = device.GetBaseBindings(stage_index);
|
|
|
|
u32 binding = device.UseAssemblyShaders() ? 0 : base_bindings.uniform_buffer;
|
|
|
|
for (const auto& entry : entries.const_buffers) {
|
|
|
|
const u32 index = entry.GetIndex();
|
|
|
|
const auto& buffer = shader_stage.const_buffers[index];
|
|
|
|
SetupConstBuffer(PARAMETER_LUT[stage_index], binding, buffer, entry, use_unified,
|
|
|
|
base_unified_offset + index * Maxwell::MaxConstBufferSize);
|
|
|
|
++binding;
|
|
|
|
}
|
|
|
|
if (use_unified) {
|
|
|
|
const u32 index = static_cast<u32>(base_bindings.shader_storage_buffer +
|
|
|
|
entries.global_memory_entries.size());
|
|
|
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, unified_uniform_buffer.handle,
|
|
|
|
base_unified_offset, NUM_CONST_BUFFERS_BYTES_PER_STAGE);
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) {
|
2019-07-15 03:25:13 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_UBO);
|
|
|
|
const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
|
2020-05-28 22:06:22 +02:00
|
|
|
const auto& entries = kernel->GetEntries();
|
|
|
|
const bool use_unified = entries.use_unified_uniforms;
|
2019-11-19 01:38:15 +01:00
|
|
|
|
|
|
|
u32 binding = 0;
|
2020-05-28 22:06:22 +02:00
|
|
|
for (const auto& entry : entries.const_buffers) {
|
2019-07-15 03:25:13 +02:00
|
|
|
const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
|
2019-07-12 02:54:07 +02:00
|
|
|
const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
|
2019-07-15 03:25:13 +02:00
|
|
|
Tegra::Engines::ConstBufferInfo buffer;
|
|
|
|
buffer.address = config.Address();
|
|
|
|
buffer.size = config.size;
|
|
|
|
buffer.enabled = mask[entry.GetIndex()];
|
2020-05-28 22:06:22 +02:00
|
|
|
SetupConstBuffer(GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV, binding, buffer, entry,
|
|
|
|
use_unified, entry.GetIndex() * Maxwell::MaxConstBufferSize);
|
|
|
|
++binding;
|
|
|
|
}
|
|
|
|
if (use_unified) {
|
|
|
|
const GLuint index = static_cast<GLuint>(entries.global_memory_entries.size());
|
|
|
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, unified_uniform_buffer.handle, 0,
|
|
|
|
NUM_CONST_BUFFERS_BYTES_PER_STAGE);
|
2019-05-31 22:33:21 +02:00
|
|
|
}
|
|
|
|
}
|
2018-08-08 08:07:44 +02:00
|
|
|
|
2020-05-18 03:32:49 +02:00
|
|
|
void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding,
|
|
|
|
const Tegra::Engines::ConstBufferInfo& buffer,
|
2020-05-28 22:06:22 +02:00
|
|
|
const ConstBufferEntry& entry, bool use_unified,
|
|
|
|
std::size_t unified_offset) {
|
2019-05-31 22:33:21 +02:00
|
|
|
if (!buffer.enabled) {
|
|
|
|
// Set values to zero to unbind buffers
|
2020-05-18 03:32:49 +02:00
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0);
|
|
|
|
} else {
|
2020-05-11 21:35:04 +02:00
|
|
|
glBindBufferRange(GL_UNIFORM_BUFFER, binding, 0, 0, sizeof(float));
|
2020-05-18 03:32:49 +02:00
|
|
|
}
|
2019-05-31 22:33:21 +02:00
|
|
|
return;
|
|
|
|
}
|
2018-06-10 01:02:05 +02:00
|
|
|
|
2019-05-31 22:33:21 +02:00
|
|
|
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
|
|
|
|
// UBO alignment requirements.
|
2019-07-06 04:11:58 +02:00
|
|
|
const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4));
|
2018-04-15 18:18:09 +02:00
|
|
|
|
2020-05-28 22:06:22 +02:00
|
|
|
const bool fast_upload = !use_unified && device.HasFastBufferSubData();
|
|
|
|
|
|
|
|
const std::size_t alignment = use_unified ? 4 : device.GetUniformBufferAlignment();
|
|
|
|
const GPUVAddr gpu_addr = buffer.address;
|
2020-05-11 21:35:04 +02:00
|
|
|
auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload);
|
2020-05-28 22:06:22 +02:00
|
|
|
|
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
UNIMPLEMENTED_IF(use_unified);
|
2020-05-11 21:35:04 +02:00
|
|
|
if (info.offset != 0) {
|
2020-05-28 22:06:22 +02:00
|
|
|
const GLuint staging_cbuf = staging_cbufs[current_cbuf++];
|
2020-05-11 21:35:04 +02:00
|
|
|
glCopyNamedBufferSubData(info.handle, staging_cbuf, info.offset, 0, size);
|
|
|
|
info.handle = staging_cbuf;
|
|
|
|
info.offset = 0;
|
2020-05-28 22:06:22 +02:00
|
|
|
}
|
2020-05-11 21:35:04 +02:00
|
|
|
glBindBufferRangeNV(stage, binding, info.handle, info.offset, size);
|
2020-05-18 03:32:49 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-05-28 22:06:22 +02:00
|
|
|
|
|
|
|
if (use_unified) {
|
2020-05-11 21:35:04 +02:00
|
|
|
glCopyNamedBufferSubData(info.handle, unified_uniform_buffer.handle, info.offset,
|
|
|
|
unified_offset, size);
|
2020-05-28 22:06:22 +02:00
|
|
|
} else {
|
2020-05-11 21:35:04 +02:00
|
|
|
glBindBufferRange(GL_UNIFORM_BUFFER, binding, info.handle, info.offset, size);
|
2020-05-18 03:32:49 +02:00
|
|
|
}
|
2018-04-14 18:50:15 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* shader) {
|
2019-05-29 23:15:28 +02:00
|
|
|
auto& gpu{system.GPU()};
|
|
|
|
auto& memory_manager{gpu.MemoryManager()};
|
2019-11-18 22:35:21 +01:00
|
|
|
const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
|
2019-11-19 01:38:15 +01:00
|
|
|
|
2020-05-18 03:32:49 +02:00
|
|
|
u32 binding =
|
|
|
|
device.UseAssemblyShaders() ? 0 : device.GetBaseBindings(stage_index).shader_storage_buffer;
|
2020-02-26 20:13:47 +01:00
|
|
|
for (const auto& entry : shader->GetEntries().global_memory_entries) {
|
2020-04-16 06:34:45 +02:00
|
|
|
const GPUVAddr addr{cbufs.const_buffers[entry.cbuf_index].address + entry.cbuf_offset};
|
|
|
|
const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)};
|
|
|
|
const u32 size{memory_manager.Read<u32>(addr + 8)};
|
2019-11-19 01:38:15 +01:00
|
|
|
SetupGlobalMemory(binding++, entry, gpu_addr, size);
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-29 23:15:28 +02:00
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) {
|
2019-07-15 03:25:13 +02:00
|
|
|
auto& gpu{system.GPU()};
|
|
|
|
auto& memory_manager{gpu.MemoryManager()};
|
|
|
|
const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
|
2019-11-19 01:38:15 +01:00
|
|
|
|
|
|
|
u32 binding = 0;
|
2020-02-26 20:13:47 +01:00
|
|
|
for (const auto& entry : kernel->GetEntries().global_memory_entries) {
|
2020-04-16 06:34:45 +02:00
|
|
|
const auto addr{cbufs[entry.cbuf_index].Address() + entry.cbuf_offset};
|
2019-07-15 03:25:13 +02:00
|
|
|
const auto gpu_addr{memory_manager.Read<u64>(addr)};
|
|
|
|
const auto size{memory_manager.Read<u32>(addr + 8)};
|
2019-11-19 01:38:15 +01:00
|
|
|
SetupGlobalMemory(binding++, entry, gpu_addr, size);
|
2019-01-05 05:01:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-26 20:13:47 +01:00
|
|
|
void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry,
|
2019-07-15 03:25:13 +02:00
|
|
|
GPUVAddr gpu_addr, std::size_t size) {
|
|
|
|
const auto alignment{device.GetShaderStorageBufferAlignment()};
|
2020-05-11 21:35:04 +02:00
|
|
|
const auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written);
|
|
|
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, info.handle, info.offset,
|
2020-04-04 07:54:55 +02:00
|
|
|
static_cast<GLsizeiptr>(size));
|
2019-07-15 03:25:13 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) {
|
2018-09-04 11:02:59 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_Texture);
|
2019-11-22 08:34:14 +01:00
|
|
|
const auto& maxwell3d = system.GPU().Maxwell3D();
|
2019-11-19 01:38:15 +01:00
|
|
|
u32 binding = device.GetBaseBindings(stage_index).sampler;
|
2020-02-26 20:13:47 +01:00
|
|
|
for (const auto& entry : shader->GetEntries().samplers) {
|
2020-02-22 23:40:26 +01:00
|
|
|
const auto shader_type = static_cast<ShaderType>(stage_index);
|
2020-04-16 06:34:45 +02:00
|
|
|
for (std::size_t i = 0; i < entry.size; ++i) {
|
2020-02-22 23:40:26 +01:00
|
|
|
const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
|
2020-01-06 16:43:13 +01:00
|
|
|
SetupTexture(binding++, texture, entry);
|
|
|
|
}
|
2018-06-06 19:58:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) {
|
2019-07-18 02:50:21 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_Texture);
|
|
|
|
const auto& compute = system.GPU().KeplerCompute();
|
2019-11-19 01:38:15 +01:00
|
|
|
u32 binding = 0;
|
2020-02-26 20:13:47 +01:00
|
|
|
for (const auto& entry : kernel->GetEntries().samplers) {
|
2020-04-16 06:34:45 +02:00
|
|
|
for (std::size_t i = 0; i < entry.size; ++i) {
|
2020-02-22 23:40:26 +01:00
|
|
|
const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i);
|
2020-01-06 16:43:13 +01:00
|
|
|
SetupTexture(binding++, texture, entry);
|
|
|
|
}
|
2019-07-18 02:50:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-06 08:32:43 +01:00
|
|
|
void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
2020-02-26 20:13:47 +01:00
|
|
|
const SamplerEntry& entry) {
|
2019-07-12 07:01:27 +02:00
|
|
|
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
|
2019-07-12 01:09:53 +02:00
|
|
|
if (!view) {
|
|
|
|
// Can occur when texture addr is null or its memory is unmapped/invalid
|
2019-12-26 08:01:11 +01:00
|
|
|
glBindSampler(binding, 0);
|
|
|
|
glBindTextureUnit(binding, 0);
|
2019-11-06 08:32:43 +01:00
|
|
|
return;
|
2019-07-12 01:09:53 +02:00
|
|
|
}
|
2020-05-26 07:17:17 +02:00
|
|
|
const GLuint handle = view->GetTexture(texture.tic.x_source, texture.tic.y_source,
|
|
|
|
texture.tic.z_source, texture.tic.w_source);
|
|
|
|
glBindTextureUnit(binding, handle);
|
|
|
|
if (!view->GetSurfaceParams().IsBuffer()) {
|
|
|
|
glBindSampler(binding, sampler_cache.GetSampler(texture.tsc));
|
2019-07-12 01:09:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) {
|
2019-11-13 04:27:12 +01:00
|
|
|
const auto& maxwell3d = system.GPU().Maxwell3D();
|
2019-11-22 08:34:14 +01:00
|
|
|
u32 binding = device.GetBaseBindings(stage_index).image;
|
2020-02-26 20:13:47 +01:00
|
|
|
for (const auto& entry : shader->GetEntries().images) {
|
2019-11-18 22:35:21 +01:00
|
|
|
const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
|
2019-11-13 04:27:12 +01:00
|
|
|
const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic;
|
2019-11-22 08:34:14 +01:00
|
|
|
SetupImage(binding++, tic, entry);
|
2019-11-13 04:27:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-23 01:55:38 +02:00
|
|
|
void RasterizerOpenGL::SetupComputeImages(Shader* shader) {
|
2019-07-12 02:54:07 +02:00
|
|
|
const auto& compute = system.GPU().KeplerCompute();
|
2019-11-22 08:34:14 +01:00
|
|
|
u32 binding = 0;
|
2020-02-26 20:13:47 +01:00
|
|
|
for (const auto& entry : shader->GetEntries().images) {
|
2019-11-13 04:27:12 +01:00
|
|
|
const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic;
|
2019-11-22 08:34:14 +01:00
|
|
|
SetupImage(binding++, tic, entry);
|
2019-07-12 07:17:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
|
2020-02-26 20:13:47 +01:00
|
|
|
const ImageEntry& entry) {
|
2019-07-12 07:17:18 +02:00
|
|
|
const auto view = texture_cache.GetImageSurface(tic, entry);
|
|
|
|
if (!view) {
|
2019-12-26 08:17:02 +01:00
|
|
|
glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
|
2019-07-12 07:17:18 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-04-16 06:34:45 +02:00
|
|
|
if (entry.is_written) {
|
2019-09-06 04:26:05 +02:00
|
|
|
view->MarkAsModified(texture_cache.Tick());
|
|
|
|
}
|
2020-05-26 07:17:17 +02:00
|
|
|
const GLuint handle = view->GetTexture(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
|
|
|
|
glBindImageTexture(binding, handle, 0, GL_TRUE, 0, GL_READ_WRITE, view->GetFormat());
|
2019-07-12 02:54:07 +02:00
|
|
|
}
|
|
|
|
|
2019-12-26 05:50:38 +01:00
|
|
|
void RasterizerOpenGL::SyncViewport() {
|
2019-12-29 02:12:12 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
const auto& regs = gpu.regs;
|
|
|
|
|
2019-12-30 05:40:27 +01:00
|
|
|
const bool dirty_viewport = flags[Dirty::Viewports];
|
2020-05-26 05:57:38 +02:00
|
|
|
const bool dirty_clip_control = flags[Dirty::ClipControl];
|
|
|
|
|
|
|
|
if (dirty_clip_control || flags[Dirty::FrontFace]) {
|
|
|
|
flags[Dirty::FrontFace] = false;
|
|
|
|
|
|
|
|
GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
|
|
|
|
if (regs.screen_y_control.triangle_rast_flip != 0 &&
|
|
|
|
regs.viewport_transform[0].scale_y < 0.0f) {
|
|
|
|
switch (mode) {
|
|
|
|
case GL_CW:
|
|
|
|
mode = GL_CCW;
|
|
|
|
break;
|
|
|
|
case GL_CCW:
|
|
|
|
mode = GL_CW;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
glFrontFace(mode);
|
|
|
|
}
|
|
|
|
|
2019-12-30 05:40:27 +01:00
|
|
|
if (dirty_viewport || flags[Dirty::ClipControl]) {
|
|
|
|
flags[Dirty::ClipControl] = false;
|
|
|
|
|
|
|
|
bool flip_y = false;
|
|
|
|
if (regs.viewport_transform[0].scale_y < 0.0) {
|
|
|
|
flip_y = !flip_y;
|
|
|
|
}
|
|
|
|
if (regs.screen_y_control.y_negate != 0) {
|
|
|
|
flip_y = !flip_y;
|
|
|
|
}
|
|
|
|
glClipControl(flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT,
|
|
|
|
regs.depth_mode == Maxwell::DepthMode::ZeroToOne ? GL_ZERO_TO_ONE
|
|
|
|
: GL_NEGATIVE_ONE_TO_ONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dirty_viewport) {
|
2019-12-29 02:12:12 +01:00
|
|
|
flags[Dirty::Viewports] = false;
|
|
|
|
|
|
|
|
const bool force = flags[Dirty::ViewportTransform];
|
|
|
|
flags[Dirty::ViewportTransform] = false;
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
|
|
|
|
if (!force && !flags[Dirty::Viewport0 + i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
flags[Dirty::Viewport0 + i] = false;
|
|
|
|
|
2020-03-22 07:26:07 +01:00
|
|
|
const auto& src = regs.viewport_transform[i];
|
|
|
|
const Common::Rectangle<f32> rect{src.GetRect()};
|
2019-12-29 02:12:12 +01:00
|
|
|
glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
|
|
|
|
rect.GetHeight());
|
|
|
|
|
2020-03-22 07:26:07 +01:00
|
|
|
const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
|
|
|
|
const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z;
|
|
|
|
const GLdouble far_depth = src.translate_z + src.scale_z;
|
|
|
|
glDepthRangeIndexed(static_cast<GLuint>(i), near_depth, far_depth);
|
2020-05-04 22:51:30 +02:00
|
|
|
|
|
|
|
if (!GLAD_GL_NV_viewport_swizzle) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
glViewportSwizzleNV(static_cast<GLuint>(i), MaxwellToGL::ViewportSwizzle(src.swizzle.x),
|
|
|
|
MaxwellToGL::ViewportSwizzle(src.swizzle.y),
|
|
|
|
MaxwellToGL::ViewportSwizzle(src.swizzle.z),
|
|
|
|
MaxwellToGL::ViewportSwizzle(src.swizzle.w));
|
2019-12-29 02:12:12 +01:00
|
|
|
}
|
2018-11-02 04:21:25 +01:00
|
|
|
}
|
2018-03-27 02:45:10 +02:00
|
|
|
}
|
|
|
|
|
2019-12-26 01:57:10 +01:00
|
|
|
void RasterizerOpenGL::SyncDepthClamp() {
|
2020-01-03 02:41:20 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::DepthClampEnabled]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::DepthClampEnabled] = false;
|
2018-11-29 20:13:13 +01:00
|
|
|
|
2020-04-28 01:50:14 +02:00
|
|
|
oglEnable(GL_DEPTH_CLAMP, gpu.regs.view_volume_clip_control.depth_clamp_disabled == 0);
|
2019-12-26 01:57:10 +01:00
|
|
|
}
|
|
|
|
|
2019-12-29 06:03:05 +01:00
|
|
|
void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
|
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::ClipDistances] = false;
|
|
|
|
|
|
|
|
clip_mask &= gpu.regs.clip_distance_enabled;
|
|
|
|
if (clip_mask == last_clip_distance_mask) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
last_clip_distance_mask = clip_mask;
|
2018-11-29 20:13:13 +01:00
|
|
|
|
|
|
|
for (std::size_t i = 0; i < Maxwell::Regs::NumClipDistances; ++i) {
|
2019-12-29 06:03:05 +01:00
|
|
|
oglEnable(static_cast<GLenum>(GL_CLIP_DISTANCE0 + i), (clip_mask >> i) & 1);
|
2018-11-29 20:13:13 +01:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncClipCoef() {
|
2018-11-29 20:13:13 +01:00
|
|
|
UNIMPLEMENTED();
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncCullMode() {
|
2019-12-29 23:23:40 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
const auto& regs = gpu.regs;
|
2018-07-03 04:22:25 +02:00
|
|
|
|
2019-12-29 23:23:40 +01:00
|
|
|
if (flags[Dirty::CullTest]) {
|
|
|
|
flags[Dirty::CullTest] = false;
|
2019-07-18 01:37:01 +02:00
|
|
|
|
2019-12-29 23:23:40 +01:00
|
|
|
if (regs.cull_test_enabled) {
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glCullFace(MaxwellToGL::CullFace(regs.cull_face));
|
|
|
|
} else {
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
}
|
2018-07-03 04:22:25 +02:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
|
|
|
|
2018-10-26 01:04:13 +02:00
|
|
|
void RasterizerOpenGL::SyncPrimitiveRestart() {
|
2019-12-30 03:25:53 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::PrimitiveRestart]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::PrimitiveRestart] = false;
|
2018-10-26 01:04:13 +02:00
|
|
|
|
2019-12-30 03:25:53 +01:00
|
|
|
if (gpu.regs.primitive_restart.enabled) {
|
|
|
|
glEnable(GL_PRIMITIVE_RESTART);
|
|
|
|
glPrimitiveRestartIndex(gpu.regs.primitive_restart.index);
|
|
|
|
} else {
|
|
|
|
glDisable(GL_PRIMITIVE_RESTART);
|
|
|
|
}
|
2018-10-26 01:04:13 +02:00
|
|
|
}
|
|
|
|
|
2018-07-02 20:33:06 +02:00
|
|
|
void RasterizerOpenGL::SyncDepthTestState() {
|
2019-12-30 02:56:21 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
2019-07-18 01:37:01 +02:00
|
|
|
|
2019-12-30 02:56:21 +01:00
|
|
|
const auto& regs = gpu.regs;
|
|
|
|
if (flags[Dirty::DepthMask]) {
|
|
|
|
flags[Dirty::DepthMask] = false;
|
|
|
|
glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
|
2019-07-18 01:37:01 +02:00
|
|
|
}
|
|
|
|
|
2019-12-30 02:56:21 +01:00
|
|
|
if (flags[Dirty::DepthTest]) {
|
|
|
|
flags[Dirty::DepthTest] = false;
|
|
|
|
if (regs.depth_test_enable) {
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthFunc(MaxwellToGL::ComparisonOp(regs.depth_test_func));
|
|
|
|
} else {
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
}
|
2019-07-18 01:37:01 +02:00
|
|
|
}
|
2018-07-02 20:33:06 +02:00
|
|
|
}
|
|
|
|
|
2018-08-22 06:35:31 +02:00
|
|
|
void RasterizerOpenGL::SyncStencilTestState() {
|
2019-12-30 03:08:32 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::StencilTest]) {
|
2018-08-22 06:35:31 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-30 03:08:32 +01:00
|
|
|
flags[Dirty::StencilTest] = false;
|
2019-09-04 04:19:31 +02:00
|
|
|
|
2019-12-30 03:08:32 +01:00
|
|
|
const auto& regs = gpu.regs;
|
2020-03-26 05:08:11 +01:00
|
|
|
oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
|
2019-07-18 01:37:01 +02:00
|
|
|
|
2019-12-26 07:34:29 +01:00
|
|
|
glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
|
|
|
|
regs.stencil_front_func_ref, regs.stencil_front_func_mask);
|
|
|
|
glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail),
|
|
|
|
MaxwellToGL::StencilOp(regs.stencil_front_op_zfail),
|
|
|
|
MaxwellToGL::StencilOp(regs.stencil_front_op_zpass));
|
|
|
|
glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask);
|
|
|
|
|
2018-11-07 04:27:12 +01:00
|
|
|
if (regs.stencil_two_side_enable) {
|
2019-12-26 07:34:29 +01:00
|
|
|
glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func),
|
|
|
|
regs.stencil_back_func_ref, regs.stencil_back_func_mask);
|
|
|
|
glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail),
|
|
|
|
MaxwellToGL::StencilOp(regs.stencil_back_op_zfail),
|
|
|
|
MaxwellToGL::StencilOp(regs.stencil_back_op_zpass));
|
|
|
|
glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask);
|
2018-11-07 04:27:12 +01:00
|
|
|
} else {
|
2019-12-26 07:34:29 +01:00
|
|
|
glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF);
|
|
|
|
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
glStencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
|
2018-11-07 04:27:12 +01:00
|
|
|
}
|
2018-08-22 06:35:31 +02:00
|
|
|
}
|
|
|
|
|
2019-12-26 06:11:01 +01:00
|
|
|
void RasterizerOpenGL::SyncRasterizeEnable() {
|
2019-12-30 04:49:19 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::RasterizeEnable]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::RasterizeEnable] = false;
|
|
|
|
|
|
|
|
oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0);
|
2019-12-18 23:26:52 +01:00
|
|
|
}
|
|
|
|
|
2020-02-24 23:43:57 +01:00
|
|
|
void RasterizerOpenGL::SyncPolygonModes() {
|
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::PolygonModes]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::PolygonModes] = false;
|
|
|
|
|
|
|
|
if (gpu.regs.fill_rectangle) {
|
|
|
|
if (!GLAD_GL_NV_fill_rectangle) {
|
|
|
|
LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags[Dirty::PolygonModeFront] = true;
|
|
|
|
flags[Dirty::PolygonModeBack] = true;
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_RECTANGLE_NV);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gpu.regs.polygon_mode_front == gpu.regs.polygon_mode_back) {
|
|
|
|
flags[Dirty::PolygonModeFront] = false;
|
|
|
|
flags[Dirty::PolygonModeBack] = false;
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags[Dirty::PolygonModeFront]) {
|
|
|
|
flags[Dirty::PolygonModeFront] = false;
|
|
|
|
glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags[Dirty::PolygonModeBack]) {
|
|
|
|
flags[Dirty::PolygonModeBack] = false;
|
|
|
|
glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_back));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 03:46:06 +01:00
|
|
|
void RasterizerOpenGL::SyncColorMask() {
|
2019-12-29 02:51:04 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::ColorMasks]) {
|
2019-07-13 22:52:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-29 02:51:04 +01:00
|
|
|
flags[Dirty::ColorMasks] = false;
|
2019-07-13 22:52:32 +02:00
|
|
|
|
2019-12-29 02:51:04 +01:00
|
|
|
const bool force = flags[Dirty::ColorMaskCommon];
|
|
|
|
flags[Dirty::ColorMaskCommon] = false;
|
2019-07-13 22:52:32 +02:00
|
|
|
|
2019-12-29 02:51:04 +01:00
|
|
|
const auto& regs = gpu.regs;
|
2019-12-26 05:19:15 +01:00
|
|
|
if (regs.color_mask_common) {
|
2019-12-29 02:51:04 +01:00
|
|
|
if (!force && !flags[Dirty::ColorMask0]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::ColorMask0] = false;
|
|
|
|
|
2019-12-26 05:19:15 +01:00
|
|
|
auto& mask = regs.color_mask[0];
|
2019-12-29 02:51:04 +01:00
|
|
|
glColorMask(mask.R != 0, mask.B != 0, mask.G != 0, mask.A != 0);
|
|
|
|
return;
|
2018-11-05 03:46:06 +01:00
|
|
|
}
|
2019-07-13 22:52:32 +02:00
|
|
|
|
2019-12-29 02:51:04 +01:00
|
|
|
// Path without color_mask_common set
|
|
|
|
for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
|
|
|
|
if (!force && !flags[Dirty::ColorMask0 + i]) {
|
|
|
|
continue;
|
2019-12-26 05:19:15 +01:00
|
|
|
}
|
2019-12-29 02:51:04 +01:00
|
|
|
flags[Dirty::ColorMask0 + i] = false;
|
|
|
|
|
|
|
|
const auto& mask = regs.color_mask[i];
|
|
|
|
glColorMaski(static_cast<GLuint>(i), mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0);
|
2018-11-05 03:46:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-14 04:02:54 +01:00
|
|
|
void RasterizerOpenGL::SyncMultiSampleState() {
|
2019-12-30 04:43:15 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::MultisampleControl]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::MultisampleControl] = false;
|
|
|
|
|
2019-03-09 07:25:11 +01:00
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
2019-12-26 05:04:36 +01:00
|
|
|
oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
|
|
|
|
oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
|
2018-11-14 04:02:54 +01:00
|
|
|
}
|
|
|
|
|
2018-11-14 02:09:01 +01:00
|
|
|
void RasterizerOpenGL::SyncFragmentColorClampState() {
|
2019-12-30 05:20:08 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::FragmentClampColor]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::FragmentClampColor] = false;
|
|
|
|
|
|
|
|
glClampColor(GL_CLAMP_FRAGMENT_COLOR, gpu.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
|
2018-11-14 02:09:01 +01:00
|
|
|
}
|
|
|
|
|
2018-06-09 00:05:52 +02:00
|
|
|
void RasterizerOpenGL::SyncBlendState() {
|
2019-12-29 22:14:40 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
const auto& regs = gpu.regs;
|
|
|
|
|
|
|
|
if (flags[Dirty::BlendColor]) {
|
|
|
|
flags[Dirty::BlendColor] = false;
|
|
|
|
glBlendColor(regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
|
|
|
|
regs.blend_color.a);
|
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
|
2019-12-29 22:14:40 +01:00
|
|
|
// TODO(Rodrigo): Revisit blending, there are several registers we are not reading
|
|
|
|
|
|
|
|
if (!flags[Dirty::BlendStates]) {
|
2019-07-13 22:52:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-29 22:14:40 +01:00
|
|
|
flags[Dirty::BlendStates] = false;
|
2018-11-02 04:21:25 +01:00
|
|
|
|
2019-12-26 07:51:50 +01:00
|
|
|
if (!regs.independent_blend_enable) {
|
2019-12-29 22:14:40 +01:00
|
|
|
if (!regs.blend.enable[0]) {
|
|
|
|
glDisable(GL_BLEND);
|
2019-12-26 07:51:50 +01:00
|
|
|
return;
|
2018-11-02 04:21:25 +01:00
|
|
|
}
|
2019-12-29 22:14:40 +01:00
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb),
|
|
|
|
MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb),
|
|
|
|
MaxwellToGL::BlendFunc(regs.blend.factor_source_a),
|
|
|
|
MaxwellToGL::BlendFunc(regs.blend.factor_dest_a));
|
|
|
|
glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb),
|
|
|
|
MaxwellToGL::BlendEquation(regs.blend.equation_a));
|
2018-06-09 00:05:52 +02:00
|
|
|
return;
|
2018-11-02 04:21:25 +01:00
|
|
|
}
|
2018-06-09 00:05:52 +02:00
|
|
|
|
2019-12-29 22:14:40 +01:00
|
|
|
const bool force = flags[Dirty::BlendIndependentEnabled];
|
|
|
|
flags[Dirty::BlendIndependentEnabled] = false;
|
|
|
|
|
2019-12-26 07:51:50 +01:00
|
|
|
for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
|
2019-12-29 22:14:40 +01:00
|
|
|
if (!force && !flags[Dirty::BlendState0 + i]) {
|
2018-11-02 04:21:25 +01:00
|
|
|
continue;
|
2019-12-29 22:14:40 +01:00
|
|
|
}
|
|
|
|
flags[Dirty::BlendState0 + i] = false;
|
|
|
|
|
2019-12-26 07:51:50 +01:00
|
|
|
if (!regs.blend.enable[i]) {
|
2019-12-29 22:14:40 +01:00
|
|
|
glDisablei(GL_BLEND, static_cast<GLuint>(i));
|
2018-11-02 04:21:25 +01:00
|
|
|
continue;
|
2019-12-26 07:51:50 +01:00
|
|
|
}
|
2019-12-29 22:14:40 +01:00
|
|
|
glEnablei(GL_BLEND, static_cast<GLuint>(i));
|
2019-07-13 22:52:32 +02:00
|
|
|
|
2019-12-26 07:51:50 +01:00
|
|
|
const auto& src = regs.independent_blend[i];
|
|
|
|
glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.factor_source_rgb),
|
|
|
|
MaxwellToGL::BlendFunc(src.factor_dest_rgb),
|
|
|
|
MaxwellToGL::BlendFunc(src.factor_source_a),
|
|
|
|
MaxwellToGL::BlendFunc(src.factor_dest_a));
|
|
|
|
glBlendEquationSeparatei(static_cast<GLuint>(i),
|
|
|
|
MaxwellToGL::BlendEquation(src.equation_rgb),
|
|
|
|
MaxwellToGL::BlendEquation(src.equation_a));
|
2018-11-02 04:21:25 +01:00
|
|
|
}
|
2018-03-20 04:00:59 +01:00
|
|
|
}
|
2018-08-21 01:44:47 +02:00
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncLogicOpState() {
|
2019-12-30 04:57:50 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::LogicOp]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::LogicOp] = false;
|
2018-08-21 01:44:47 +02:00
|
|
|
|
2019-12-30 04:57:50 +01:00
|
|
|
const auto& regs = gpu.regs;
|
2019-12-26 00:21:53 +01:00
|
|
|
if (regs.logic_op.enable) {
|
2019-12-30 04:57:50 +01:00
|
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
2019-12-26 00:21:53 +01:00
|
|
|
glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
|
2019-12-30 04:57:50 +01:00
|
|
|
} else {
|
|
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
2019-12-26 00:21:53 +01:00
|
|
|
}
|
2018-08-21 01:44:47 +02:00
|
|
|
}
|
|
|
|
|
2019-12-26 05:28:17 +01:00
|
|
|
void RasterizerOpenGL::SyncScissorTest() {
|
2019-12-29 02:31:00 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::Scissors]) {
|
2018-08-21 01:44:47 +02:00
|
|
|
return;
|
2019-12-29 02:31:00 +01:00
|
|
|
}
|
|
|
|
flags[Dirty::Scissors] = false;
|
2018-08-21 01:44:47 +02:00
|
|
|
|
2019-12-29 02:31:00 +01:00
|
|
|
const auto& regs = gpu.regs;
|
2019-12-26 05:28:17 +01:00
|
|
|
for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
|
2019-12-29 02:31:00 +01:00
|
|
|
if (!flags[Dirty::Scissor0 + index]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
flags[Dirty::Scissor0 + index] = false;
|
2018-08-21 10:18:27 +02:00
|
|
|
|
2019-12-26 05:28:17 +01:00
|
|
|
const auto& src = regs.scissor_test[index];
|
2019-12-29 02:31:00 +01:00
|
|
|
if (src.enable) {
|
|
|
|
glEnablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
|
|
|
|
glScissorIndexed(static_cast<GLuint>(index), src.min_x, src.min_y,
|
|
|
|
src.max_x - src.min_x, src.max_y - src.min_y);
|
|
|
|
} else {
|
|
|
|
glDisablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
|
2018-11-14 00:13:16 +01:00
|
|
|
}
|
2018-10-09 02:49:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-28 06:31:01 +02:00
|
|
|
void RasterizerOpenGL::SyncPointState() {
|
2019-12-30 05:27:42 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::PointSize]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::PointSize] = false;
|
|
|
|
|
|
|
|
oglEnable(GL_POINT_SPRITE, gpu.regs.point_sprite_enable);
|
|
|
|
|
|
|
|
if (gpu.regs.vp_point_size.enable) {
|
|
|
|
// By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled.
|
|
|
|
glEnable(GL_PROGRAM_POINT_SIZE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-18 08:02:34 +02:00
|
|
|
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
|
|
|
|
// in OpenGL).
|
2019-12-30 05:27:42 +01:00
|
|
|
glPointSize(std::max(1.0f, gpu.regs.point_size));
|
|
|
|
glDisable(GL_PROGRAM_POINT_SIZE);
|
2018-09-28 06:31:01 +02:00
|
|
|
}
|
|
|
|
|
2020-02-25 01:02:32 +01:00
|
|
|
void RasterizerOpenGL::SyncLineState() {
|
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::LineWidth]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::LineWidth] = false;
|
|
|
|
|
|
|
|
const auto& regs = gpu.regs;
|
|
|
|
oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable);
|
|
|
|
glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased);
|
|
|
|
}
|
|
|
|
|
2018-11-27 00:31:44 +01:00
|
|
|
void RasterizerOpenGL::SyncPolygonOffset() {
|
2019-12-30 04:22:43 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::PolygonOffset]) {
|
2019-07-13 22:52:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-30 04:22:43 +01:00
|
|
|
flags[Dirty::PolygonOffset] = false;
|
2019-10-01 07:00:23 +02:00
|
|
|
|
2019-12-30 04:22:43 +01:00
|
|
|
const auto& regs = gpu.regs;
|
2019-12-26 04:25:53 +01:00
|
|
|
oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
|
|
|
|
oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
|
|
|
|
oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
|
2019-07-13 22:52:32 +02:00
|
|
|
|
2020-01-03 02:42:56 +01:00
|
|
|
if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable ||
|
|
|
|
regs.polygon_offset_point_enable) {
|
|
|
|
// Hardware divides polygon offset units by two
|
|
|
|
glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f,
|
|
|
|
regs.polygon_offset_clamp);
|
|
|
|
}
|
2018-11-27 00:31:44 +01:00
|
|
|
}
|
|
|
|
|
2019-05-22 01:28:09 +02:00
|
|
|
void RasterizerOpenGL::SyncAlphaTest() {
|
2019-12-30 04:37:35 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::AlphaTest]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
flags[Dirty::AlphaTest] = false;
|
|
|
|
|
|
|
|
const auto& regs = gpu.regs;
|
|
|
|
if (regs.alpha_test_enabled && regs.rt_control.count > 1) {
|
|
|
|
LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested");
|
|
|
|
}
|
2019-05-22 01:28:09 +02:00
|
|
|
|
2019-12-26 00:03:40 +01:00
|
|
|
if (regs.alpha_test_enabled) {
|
2019-12-30 04:37:35 +01:00
|
|
|
glEnable(GL_ALPHA_TEST);
|
2019-12-26 00:03:40 +01:00
|
|
|
glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
|
2019-12-30 04:37:35 +01:00
|
|
|
} else {
|
|
|
|
glDisable(GL_ALPHA_TEST);
|
2019-05-22 01:28:09 +02:00
|
|
|
}
|
2018-10-12 02:29:11 +02:00
|
|
|
}
|
|
|
|
|
2019-12-26 05:01:41 +01:00
|
|
|
void RasterizerOpenGL::SyncFramebufferSRGB() {
|
2019-12-30 04:53:53 +01:00
|
|
|
auto& gpu = system.GPU().Maxwell3D();
|
|
|
|
auto& flags = gpu.dirty.flags;
|
|
|
|
if (!flags[Dirty::FramebufferSRGB]) {
|
2019-05-22 01:28:09 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-12-30 04:53:53 +01:00
|
|
|
flags[Dirty::FramebufferSRGB] = false;
|
|
|
|
|
|
|
|
oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
|
2018-10-12 02:29:11 +02:00
|
|
|
}
|
|
|
|
|
2020-06-03 07:48:41 +02:00
|
|
|
void RasterizerOpenGL::SyncTransformFeedback() {
|
|
|
|
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
|
|
|
|
// when this is required.
|
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
|
|
|
|
|
|
|
static constexpr std::size_t STRIDE = 3;
|
|
|
|
std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs;
|
|
|
|
std::array<GLint, Maxwell::NumTransformFeedbackBuffers> streams;
|
|
|
|
|
|
|
|
GLint* cursor = attribs.data();
|
|
|
|
GLint* current_stream = streams.data();
|
|
|
|
|
|
|
|
for (std::size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
|
|
|
|
const auto& layout = regs.tfb_layouts[feedback];
|
|
|
|
UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
|
|
|
|
if (layout.varying_count == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
*current_stream = static_cast<GLint>(feedback);
|
|
|
|
if (current_stream != streams.data()) {
|
|
|
|
// When stepping one stream, push the expected token
|
|
|
|
cursor[0] = GL_NEXT_BUFFER_NV;
|
|
|
|
cursor[1] = 0;
|
|
|
|
cursor[2] = 0;
|
|
|
|
cursor += STRIDE;
|
|
|
|
}
|
|
|
|
++current_stream;
|
|
|
|
|
|
|
|
const auto& locations = regs.tfb_varying_locs[feedback];
|
|
|
|
std::optional<u8> current_index;
|
|
|
|
for (u32 offset = 0; offset < layout.varying_count; ++offset) {
|
|
|
|
const u8 location = locations[offset];
|
|
|
|
const u8 index = location / 4;
|
|
|
|
|
|
|
|
if (current_index == index) {
|
|
|
|
// Increase number of components of the previous attachment
|
|
|
|
++cursor[-2];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
current_index = index;
|
|
|
|
|
|
|
|
std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location);
|
|
|
|
cursor[1] = 1;
|
|
|
|
cursor += STRIDE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const GLsizei num_attribs = static_cast<GLsizei>((cursor - attribs.data()) / STRIDE);
|
|
|
|
const GLsizei num_strides = static_cast<GLsizei>(current_stream - streams.data());
|
|
|
|
glTransformFeedbackStreamAttribsNV(num_attribs, attribs.data(), num_strides, streams.data(),
|
|
|
|
GL_INTERLEAVED_ATTRIBS);
|
|
|
|
}
|
|
|
|
|
2020-03-02 23:31:26 +01:00
|
|
|
void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
|
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
|
|
|
if (regs.tfb_enabled == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-03 07:48:41 +02:00
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
SyncTransformFeedback();
|
|
|
|
}
|
|
|
|
|
2020-03-02 23:31:26 +01:00
|
|
|
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
|
|
|
|
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
|
|
|
|
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < Maxwell::NumTransformFeedbackBuffers; ++index) {
|
|
|
|
const auto& binding = regs.tfb_bindings[index];
|
|
|
|
if (!binding.buffer_enable) {
|
|
|
|
if (enabled_transform_feedback_buffers[index]) {
|
|
|
|
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(index), 0, 0,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
enabled_transform_feedback_buffers[index] = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
enabled_transform_feedback_buffers[index] = true;
|
|
|
|
|
|
|
|
auto& tfb_buffer = transform_feedback_buffers[index];
|
|
|
|
tfb_buffer.Create();
|
|
|
|
|
|
|
|
const GLuint handle = tfb_buffer.handle;
|
|
|
|
const std::size_t size = binding.buffer_size;
|
|
|
|
glNamedBufferData(handle, static_cast<GLsizeiptr>(size), nullptr, GL_STREAM_COPY);
|
|
|
|
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(index), handle, 0,
|
|
|
|
static_cast<GLsizeiptr>(size));
|
|
|
|
}
|
|
|
|
|
2020-06-03 07:48:41 +02:00
|
|
|
// We may have to call BeginTransformFeedbackNV here since they seem to call different
|
|
|
|
// implementations on Nvidia's driver (the pointer is different) but we are using
|
|
|
|
// ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
|
|
|
|
// extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
|
2020-03-02 23:31:26 +01:00
|
|
|
glBeginTransformFeedback(GL_POINTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerOpenGL::EndTransformFeedback() {
|
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
|
|
|
if (regs.tfb_enabled == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
glEndTransformFeedback();
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < Maxwell::NumTransformFeedbackBuffers; ++index) {
|
|
|
|
const auto& binding = regs.tfb_bindings[index];
|
|
|
|
if (!binding.buffer_enable) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_IF(binding.buffer_offset != 0);
|
|
|
|
|
|
|
|
const GLuint handle = transform_feedback_buffers[index].handle;
|
|
|
|
const GPUVAddr gpu_addr = binding.Address();
|
|
|
|
const std::size_t size = binding.buffer_size;
|
2020-05-11 21:35:04 +02:00
|
|
|
const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true);
|
|
|
|
glCopyNamedBufferSubData(handle, info.handle, 0, info.offset,
|
|
|
|
static_cast<GLsizeiptr>(size));
|
2020-03-02 23:31:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-21 10:18:27 +02:00
|
|
|
} // namespace OpenGL
|