-
-
Notifications
You must be signed in to change notification settings - Fork 382
nv2a/pgraph: refactor blit into shared implementation with GL/VK wrap… #2675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
nv2a/pgraph: refactor blit into shared implementation with GL/VK wrap… #2675
Conversation
Summary of ChangesHello @KraftMacAndChee, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly refactors the image blitting functionality within the NV2A PGRAPH component by extracting the core blit logic into a shared, renderer-agnostic implementation. This change eliminates code duplication between the OpenGL and Vulkan rendering backends, making the blitting process more maintainable and easier to extend in the future. The renderer-specific files now act as lightweight wrappers, providing their unique surface management functions to the unified blit routine. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request is a great refactoring that consolidates duplicated image blitting logic from the OpenGL and Vulkan backends into a shared implementation. This significantly improves maintainability. I've reviewed the new common implementation and found a few potential correctness issues that were likely present in the original code. My comments below provide suggestions to make the implementation more robust.
| PGRAPHState *pg = &d->pgraph; | ||
| ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d; | ||
| ImageBlitState *image_blit = &pg->image_blit; | ||
| BetaState *beta = &pg->beta; | ||
|
|
||
| ops->surface_update(d, false, true, true); | ||
|
|
||
| assert(context_surfaces->object_instance == image_blit->context_surfaces); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blits with zero width or height should be handled as no-ops. The current code proceeds with calculations that can lead to integer underflow when calculating buffer sizes (e.g., image_blit->height - 1 when height is 0). This can cause the bounds-checking assertions to fail or behave incorrectly.
It's best to add an early return at the beginning of the function if the width or height is zero.
PGRAPHState *pg = &d->pgraph;
ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d;
ImageBlitState *image_blit = &pg->image_blit;
if (image_blit->width == 0 || image_blit->height == 0) {
return;
}
BetaState *beta = &pg->beta;
ops->surface_update(d, false, true, true);
assert(context_surfaces->object_instance == image_blit->context_surfaces);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably does make sense to bail early on zero-sized draws. I don't think we perform any interesting side effects that wouldn't be covered elsewhere in the pipeline.
hw/xbox/nv2a/pgraph/blit.c
Outdated
| dest_row += context_surfaces->dest_pitch; | ||
| } | ||
| } else if (image_blit->operation == NV09F_SET_OPERATION_BLEND_AND) { | ||
| // NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_BLEND_AND"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BLEND_AND operation implementation assumes a 32bpp format by using x * 4 for pixel addressing. This will cause incorrect memory access if used with formats that have a different number of bytes per pixel (e.g., 1 or 2).
An assertion should be added to ensure this operation is only used with 32bpp formats.
assert(bytes_per_pixel == 4 && "BLEND_AND is only implemented for 32bpp formats");
hw/xbox/nv2a/pgraph/blit.c
Outdated
| uint32_t beta_mult = beta->beta >> 16; | ||
| uint32_t inv_beta_mult = max_beta_mult - beta_mult; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If beta_mult is greater than max_beta_mult, inv_beta_mult will wrap around due to unsigned integer arithmetic, resulting in a very large positive value. This will lead to incorrect blending calculations.
beta_mult should be clamped to max_beta_mult to prevent this.
uint32_t beta_mult = MIN(beta->beta >> 16, max_beta_mult);
uint32_t inv_beta_mult = max_beta_mult - beta_mult;
abaire
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly nitpicky comments
hw/xbox/nv2a/pgraph/gl/blit.c
Outdated
| #include "renderer.h" | ||
| #include "hw/xbox/nv2a/pgraph/blit.h" | ||
|
|
||
| // TODO: Optimize. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this comment now. There's not much that could be done to optimize the one function call and the comment doesn't give any detail on how to do renderer-specific optimization.
hw/xbox/nv2a/pgraph/blit.c
Outdated
| #include "hw/xbox/nv2a/pgraph/blit.h" | ||
|
|
||
|
|
||
| // TODO: Optimize. Ideally this should all be done via OpenGL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove, now that this is a shared implementation it doesn't make sense to suggest GL.
| PGRAPHState *pg = &d->pgraph; | ||
| ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d; | ||
| ImageBlitState *image_blit = &pg->image_blit; | ||
| BetaState *beta = &pg->beta; | ||
|
|
||
| ops->surface_update(d, false, true, true); | ||
|
|
||
| assert(context_surfaces->object_instance == image_blit->context_surfaces); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably does make sense to bail early on zero-sized draws. I don't think we perform any interesting side effects that wouldn't be covered elsewhere in the pipeline.
hw/xbox/nv2a/pgraph/blit.c
Outdated
| uint8_t *dest_row = dest + dest_offset; | ||
|
|
||
| if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { | ||
| // NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_SRCCOPY"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: While you're in here, we can probably lose these commented-out DPRINTF's as well.
hw/xbox/nv2a/pgraph/blit.h
Outdated
| @@ -0,0 +1,13 @@ | |||
| // Header for blit.c | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should bring over the comment from the original GL impl.
hw/xbox/nv2a/pgraph/blit.h
Outdated
| @@ -0,0 +1,13 @@ | |||
| // Header for blit.c | |||
|
|
|||
| #pragma once | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we use pragma once outside of C++ files. Would be good to use old C-style guards like in
Line 21 in 24087af
| #ifndef HW_NV2A_REGS_H |
hw/xbox/nv2a/pgraph/blit.h
Outdated
|
|
||
| #pragma once | ||
|
|
||
| #include "hw/xbox/nv2a/nv2a_int.h" // NV2AState, hwaddr, SurfaceBinding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I don't think we declare the types used from headers elsewhere and these can easily go stale without automation. I think we should remove them.
hw/xbox/nv2a/pgraph/blit.h
Outdated
|
|
||
| #include "hw/xbox/nv2a/nv2a_int.h" // NV2AState, hwaddr, SurfaceBinding | ||
|
|
||
| typedef struct PGRAPHSurfaceOps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should put this in the PGRAPHRenderer so they're available everywhere?
xemu/hw/xbox/nv2a/pgraph/pgraph.h
Line 108 in 24087af
| typedef struct PGRAPHRenderer { |
hw/xbox/nv2a/pgraph/gl/blit.c
Outdated
| void pgraph_gl_image_blit(NV2AState *d) | ||
| { | ||
| static const PGRAPHSurfaceOps ops = { | ||
| .surface_update = pgraph_gl_surface_update, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this even simpler and drop the extra indirection here.
surface_updateis already a renderer op- Just add
surface_get, addsurface_download_if_dirtyand removeimage_blitinPGRAPHRenderer::ops - Drop gl/blit.c, vk/blit.c, blit.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the patch. See comments about rebase and structure inline. Also don't worry about trying to fix pre-existing bugs with this patch. I've not evaluated the feedback of Gemini, but we should focus only on the unification in this patch. The delta between pgraph/blit.c, gl/blit.c, and vk/blit.c should be near 0. We can fix any bugs in the current implementation afterwards.
b7aad53 to
f74e009
Compare
Used a shared implementation and dropped gl/blit.c. vk/blit.c, and blit.h
I rebased it as you requested, I then worked to unify everything following your instruction and also removed gl/blit.c, vk/blit.c, and blit.h as requested. I spent a long time looking everything over and trying to double check myself and make sure I did everything correctly. However, I would appreciate you looking over it as I'm still getting my feet wet on this kind of stuff again. |
abaire
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still looks like it has dropped all of the changes from 24087af
You can use https://github.com/abaire/xemu-dev_pgraph_test_results to look for other regressions (I haven't had a chance to update the goldens for the latest xemu yet, so it wouldn't catch the blit regression that this PR will cause).
Ah yes, you’re right. I wasn’t thinking and just rebased it without modifying anything. So it would be a regression. I’ll have to fix that when I get time. |
| #include "hw/xbox/nv2a/nv2a_int.h" | ||
| #include "renderer.h" | ||
|
|
||
| #include "hw/xbox/nv2a/pgraph/surface.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already included (via nv2a_int.h, which includes pgraph.h, which includes surface.h)
| dest += dest_pitch; | ||
| } | ||
| } | ||
| void pgraph_common_image_blit(NV2AState *d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Fix whitespace
- Use name
pgraph_image_blit(this was the former name, and since there is only one 'common' is redundant)
|
|
||
| if (image_blit->width && image_blit->height) { | ||
| d->pgraph.renderer->ops.image_blit(d); | ||
| pgraph_common_image_blit(d); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should've pointed it out before, but "common" seems unnecessary if there are no renderer-spec. Maybe just "pgraph_image_blit"?
| } | ||
| } | ||
| void pgraph_common_image_blit(NV2AState *d) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove newline
| break; | ||
| } | ||
| hwaddr source_dma_len, dest_dma_len; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is (I assume inadvertently) reverted from the split version in the current head, see line 104 and 111 in the diff.
| leftover_bytes / bytes_per_pixel, 1, leftover_bytes, | ||
| context_surfaces->source_pitch, | ||
| context_surfaces->dest_pitch, beta); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This indentation is wrong.
Since this is a new file you can probably just run clang-format on it to avoid having to waste time with whitespace changes. I believe I clang-formatted the original blit file when I fixed the clipping bug.
| SurfaceBinding *surf_dest = pg->renderer->ops.surface_get(d, dest_addr); | ||
| if (surf_dest) { | ||
| if (adjusted_height < surf_dest->height || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This indentation looks wrong
| dest += dest_pitch; | ||
| } | ||
| } | ||
| void pgraph_common_image_blit(NV2AState *d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the newline above this back
Fixes #2671