summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/video_core/engines/maxwell_3d.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp70
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h14
3 files changed, 83 insertions, 10 deletions
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 8752a1cfb..ba9c76593 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -634,6 +634,11 @@ public:
u32 address_low;
s32 buffer_size;
s32 buffer_offset;
+
+ GPUVAddr Address() const {
+ return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
+ address_low);
+ }
};
static_assert(sizeof(TransformFeedbackBinding) == 32);
@@ -652,6 +657,10 @@ public:
return shader_config[index].enable != 0;
}
+ bool IsShaderConfigEnabled(Regs::ShaderProgram type) const {
+ return IsShaderConfigEnabled(static_cast<std::size_t>(type));
+ }
+
union {
struct {
INSERT_UNION_PADDING_WORDS(0x45);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8a2db8e36..1af4268a4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -496,7 +496,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
SyncCullMode();
SyncPrimitiveRestart();
SyncScissorTest();
- SyncTransformFeedback();
SyncPointState();
SyncPolygonOffset();
SyncAlphaTest();
@@ -569,7 +568,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
glTextureBarrier();
}
- ++num_queued_commands;
+ BeginTransformFeedback(primitive_mode);
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
const GLsizei num_instances =
@@ -608,6 +607,10 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
num_instances, base_instance);
}
}
+
+ EndTransformFeedback();
+
+ ++num_queued_commands;
}
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
@@ -1290,11 +1293,6 @@ void RasterizerOpenGL::SyncScissorTest() {
}
}
-void RasterizerOpenGL::SyncTransformFeedback() {
- const auto& regs = system.GPU().Maxwell3D().regs;
- UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented");
-}
-
void RasterizerOpenGL::SyncPointState() {
auto& gpu = system.GPU().Maxwell3D();
auto& flags = gpu.dirty.flags;
@@ -1370,4 +1368,62 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
}
+void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
+ const auto& regs = system.GPU().Maxwell3D().regs;
+ if (regs.tfb_enabled == 0) {
+ return;
+ }
+
+ 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));
+ }
+
+ 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;
+ const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true);
+ glCopyNamedBufferSubData(handle, *dest_buffer, 0, offset, static_cast<GLsizeiptr>(size));
+ }
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index e6424f5d2..2d3be2437 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -168,9 +168,6 @@ private:
/// Syncs the scissor test state to match the guest state
void SyncScissorTest();
- /// Syncs the transform feedback state to match the guest state
- void SyncTransformFeedback();
-
/// Syncs the point state to match the guest state
void SyncPointState();
@@ -192,6 +189,12 @@ private:
/// Syncs the framebuffer sRGB state to match the guest state
void SyncFramebufferSRGB();
+ /// Begin a transform feedback
+ void BeginTransformFeedback(GLenum primitive_mode);
+
+ /// End a transform feedback
+ void EndTransformFeedback();
+
/// Check for extension that are not strictly required but are needed for correct emulation
void CheckExtensions();
@@ -229,6 +232,11 @@ private:
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
+ std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
+ transform_feedback_buffers;
+ std::bitset<Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
+ enabled_transform_feedback_buffers;
+
/// Number of commands queued to the OpenGL driver. Reseted on flush.
std::size_t num_queued_commands = 0;