diff --git a/hw/xbox/nv2a/pgraph/gl/blit.c b/hw/xbox/nv2a/pgraph/blit.c
similarity index 79%
rename from hw/xbox/nv2a/pgraph/gl/blit.c
rename to hw/xbox/nv2a/pgraph/blit.c
index 6850ffb7c7d..ca5ab1d9289 100644
--- a/hw/xbox/nv2a/pgraph/gl/blit.c
+++ b/hw/xbox/nv2a/pgraph/blit.c
@@ -1,223 +1,223 @@
-/*
- * Geforce NV2A PGRAPH OpenGL Renderer
- *
- * Copyright (c) 2012 espes
- * Copyright (c) 2015 Jannik Vogel
- * Copyright (c) 2018-2024 Matt Borgerson
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see .
- */
-
-#include "hw/xbox/nv2a/nv2a_int.h"
-#include "renderer.h"
-
-static void perform_blit(int operation, uint8_t *source, uint8_t *dest,
- size_t width, size_t height, size_t width_bytes,
- size_t source_pitch, size_t dest_pitch,
- BetaState *beta)
-{
- if (operation == NV09F_SET_OPERATION_SRCCOPY) {
- for (unsigned int y = 0; y < height; y++) {
- memmove(dest, source, width_bytes);
- source += source_pitch;
- dest += dest_pitch;
- }
- } else if (operation == NV09F_SET_OPERATION_BLEND_AND) {
- uint32_t max_beta_mult = 0x7f80;
- uint32_t beta_mult = beta->beta >> 16;
- uint32_t inv_beta_mult = max_beta_mult - beta_mult;
-
- for (unsigned int y = 0; y < height; y++) {
- uint8_t *s = source;
- uint8_t *d = dest;
- for (unsigned int x = 0; x < width; x++) {
- for (unsigned int ch = 0; ch < 3; ch++) {
- uint32_t a = s[x * 4 + ch] * beta_mult;
- uint32_t b = d[x * 4 + ch] * inv_beta_mult;
- d[x * 4 + ch] = (a + b) / max_beta_mult;
- }
- }
- source += source_pitch;
- dest += dest_pitch;
- }
- } else {
- fprintf(stderr, "Unknown blit operation: 0x%x\n", operation);
- assert(false && "Unknown blit operation");
- }
-}
-
-static void patch_alpha(uint8_t *dest, size_t width_pixels, size_t height,
- size_t dest_pitch, uint8_t alpha_val)
-{
- for (unsigned int y = 0; y < height; y++) {
- uint8_t *d = dest;
- for (unsigned int x = 0; x < width_pixels; x++) {
- d[x * 4 + 3] = alpha_val;
- }
- dest += dest_pitch;
- }
-}
-
-void pgraph_gl_image_blit(NV2AState *d)
-{
- PGRAPHState *pg = &d->pgraph;
- ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d;
- ImageBlitState *image_blit = &pg->image_blit;
- BetaState *beta = &pg->beta;
-
- pgraph_gl_surface_update(d, false, true, true);
-
- assert(context_surfaces->object_instance == image_blit->context_surfaces);
-
- unsigned int bytes_per_pixel;
- switch (context_surfaces->color_format) {
- case NV062_SET_COLOR_FORMAT_LE_Y8:
- bytes_per_pixel = 1;
- break;
- case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
- bytes_per_pixel = 2;
- break;
- case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
- case NV062_SET_COLOR_FORMAT_LE_Y32:
- bytes_per_pixel = 4;
- break;
- default:
- fprintf(stderr, "Unknown blit surface format: 0x%x\n",
- context_surfaces->color_format);
- assert(false);
- break;
- }
-
- hwaddr source_dma_len;
- uint8_t *source = (uint8_t *)nv_dma_map(
- d, context_surfaces->dma_image_source, &source_dma_len);
- assert(context_surfaces->source_offset < source_dma_len);
- source += context_surfaces->source_offset;
- hwaddr source_addr = source - d->vram_ptr;
-
- hwaddr dest_dma_len;
- uint8_t *dest = (uint8_t *)nv_dma_map(d, context_surfaces->dma_image_dest,
- &dest_dma_len);
- assert(context_surfaces->dest_offset < dest_dma_len);
- dest += context_surfaces->dest_offset;
- hwaddr dest_addr = dest - d->vram_ptr;
-
- SurfaceBinding *surf_src = pgraph_gl_surface_get(d, source_addr);
- if (surf_src) {
- pgraph_gl_surface_download_if_dirty(d, surf_src);
- }
-
- hwaddr source_offset = image_blit->in_y * context_surfaces->source_pitch +
- image_blit->in_x * bytes_per_pixel;
- hwaddr dest_offset = image_blit->out_y * context_surfaces->dest_pitch +
- image_blit->out_x * bytes_per_pixel;
-
- size_t max_row_pixels =
- MIN(context_surfaces->source_pitch, context_surfaces->dest_pitch) /
- bytes_per_pixel;
- size_t row_pixels = MIN(max_row_pixels, image_blit->width);
-
- hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch +
- image_blit->width * bytes_per_pixel;
-
- uint8_t *source_row = source + source_offset;
- uint8_t *dest_row = dest + dest_offset;
- size_t row_bytes = row_pixels * bytes_per_pixel;
-
- size_t adjusted_height = image_blit->height;
- size_t leftover_bytes = 0;
-
- hwaddr clipped_dest_size =
- nv_clip_gpu_tile_blit(d, dest_addr + dest_offset, dest_size);
-
- if (clipped_dest_size < dest_size) {
- adjusted_height = clipped_dest_size / context_surfaces->dest_pitch;
- size_t consumed_bytes = adjusted_height * context_surfaces->dest_pitch;
-
- leftover_bytes = clipped_dest_size - consumed_bytes;
- }
-
- SurfaceBinding *surf_dest = pgraph_gl_surface_get(d, dest_addr);
- if (surf_dest) {
- if (adjusted_height < surf_dest->height ||
- row_pixels < surf_dest->width) {
- pgraph_gl_surface_download_if_dirty(d, surf_dest);
- } else {
- // The blit will completely replace the surface so any pending
- // download should be discarded.
- surf_dest->download_pending = false;
- surf_dest->draw_dirty = false;
- }
- surf_dest->upload_pending = true;
- pg->draw_time++;
- }
-
- NV2A_DPRINTF(" blit 0x%tx -> 0x%tx (Size: %llu, Clipped Height: %zu)\n",
- source_addr, dest_addr, dest_size, adjusted_height);
-
- if (adjusted_height > 0) {
- perform_blit(image_blit->operation, source_row, dest_row, row_pixels,
- adjusted_height, row_bytes, context_surfaces->source_pitch,
- context_surfaces->dest_pitch, beta);
- }
-
- if (leftover_bytes > 0) {
- uint8_t *src =
- source_row + adjusted_height * context_surfaces->source_pitch;
- uint8_t *dest =
- dest_row + adjusted_height * context_surfaces->dest_pitch;
-
- perform_blit(image_blit->operation, src, dest,
- leftover_bytes / bytes_per_pixel, 1, leftover_bytes,
- context_surfaces->source_pitch,
- context_surfaces->dest_pitch, beta);
- }
-
- bool needs_alpha_patching;
- uint8_t alpha_override;
- switch (context_surfaces->color_format) {
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
- needs_alpha_patching = true;
- alpha_override = 0xff;
- break;
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
- needs_alpha_patching = true;
- alpha_override = 0;
- break;
- default:
- needs_alpha_patching = false;
- alpha_override = 0;
- }
-
- if (needs_alpha_patching) {
- if (adjusted_height > 0) {
- patch_alpha(dest_row, row_pixels, adjusted_height,
- context_surfaces->dest_pitch, alpha_override);
- }
-
- if (leftover_bytes > 0) {
- uint8_t *dest =
- dest_row + adjusted_height * context_surfaces->dest_pitch;
- patch_alpha(dest, leftover_bytes / 4, 1, 0, alpha_override);
- }
- }
-
- dest_addr += dest_offset;
- memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size,
- DIRTY_MEMORY_VGA);
- memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size,
- DIRTY_MEMORY_NV2A_TEX);
-}
+/*
+ * Geforce NV2A PGRAPH Renderer
+ *
+ * Copyright (c) 2012 espes
+ * Copyright (c) 2015 Jannik Vogel
+ * Copyright (c) 2018-2024 Matt Borgerson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include "hw/xbox/nv2a/nv2a_int.h"
+
+static void perform_blit(int operation, uint8_t *source, uint8_t *dest,
+ size_t width, size_t height, size_t width_bytes,
+ size_t source_pitch, size_t dest_pitch,
+ BetaState *beta)
+{
+ if (operation == NV09F_SET_OPERATION_SRCCOPY) {
+ for (unsigned int y = 0; y < height; y++) {
+ memmove(dest, source, width_bytes);
+ source += source_pitch;
+ dest += dest_pitch;
+ }
+ } else if (operation == NV09F_SET_OPERATION_BLEND_AND) {
+ uint32_t max_beta_mult = 0x7f80;
+ uint32_t beta_mult = beta->beta >> 16;
+ uint32_t inv_beta_mult = max_beta_mult - beta_mult;
+
+ for (unsigned int y = 0; y < height; y++) {
+ uint8_t *s = source;
+ uint8_t *d = dest;
+ for (unsigned int x = 0; x < width; x++) {
+ for (unsigned int ch = 0; ch < 3; ch++) {
+ uint32_t a = s[x * 4 + ch] * beta_mult;
+ uint32_t b = d[x * 4 + ch] * inv_beta_mult;
+ d[x * 4 + ch] = (a + b) / max_beta_mult;
+ }
+ }
+ source += source_pitch;
+ dest += dest_pitch;
+ }
+ } else {
+ fprintf(stderr, "Unknown blit operation: 0x%x\n", operation);
+ assert(false && "Unknown blit operation");
+ }
+}
+
+static void patch_alpha(uint8_t *dest, size_t width_pixels, size_t height,
+ size_t dest_pitch, uint8_t alpha_val)
+{
+ for (unsigned int y = 0; y < height; y++) {
+ uint8_t *d = dest;
+ for (unsigned int x = 0; x < width_pixels; x++) {
+ d[x * 4 + 3] = alpha_val;
+ }
+ dest += dest_pitch;
+ }
+}
+void pgraph_common_image_blit(NV2AState *d)
+
+{
+ PGRAPHState *pg = &d->pgraph;
+ ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d;
+ ImageBlitState *image_blit = &pg->image_blit;
+ BetaState *beta = &pg->beta;
+
+ pg->renderer->ops.surface_update(d, false, true, true);
+
+ assert(context_surfaces->object_instance == image_blit->context_surfaces);
+
+ unsigned int bytes_per_pixel;
+ switch (context_surfaces->color_format) {
+ case NV062_SET_COLOR_FORMAT_LE_Y8:
+ bytes_per_pixel = 1;
+ break;
+ case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
+ bytes_per_pixel = 2;
+ break;
+ case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
+ case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
+ case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
+ case NV062_SET_COLOR_FORMAT_LE_Y32:
+ bytes_per_pixel = 4;
+ break;
+ default:
+ fprintf(stderr, "Unknown blit surface format: 0x%x\n",
+ context_surfaces->color_format);
+ assert(false);
+ break;
+ }
+
+ hwaddr source_dma_len, dest_dma_len;
+
+ uint8_t *source = (uint8_t *)nv_dma_map(
+ d, context_surfaces->dma_image_source, &source_dma_len);
+ assert(context_surfaces->source_offset < source_dma_len);
+ source += context_surfaces->source_offset;
+
+ uint8_t *dest = (uint8_t *)nv_dma_map(d, context_surfaces->dma_image_dest,
+ &dest_dma_len);
+ assert(context_surfaces->dest_offset < dest_dma_len);
+ dest += context_surfaces->dest_offset;
+
+ hwaddr source_addr = source - d->vram_ptr;
+ hwaddr dest_addr = dest - d->vram_ptr;
+
+ SurfaceBinding *surf_src = pg->renderer->ops.surface_get(d, source_addr);
+ if (surf_src) {
+ pg->renderer->ops.surface_download_if_dirty(d, surf_src);
+ }
+
+ hwaddr source_offset = image_blit->in_y * context_surfaces->source_pitch +
+ image_blit->in_x * bytes_per_pixel;
+ hwaddr dest_offset = image_blit->out_y * context_surfaces->dest_pitch +
+ image_blit->out_x * bytes_per_pixel;
+
+ size_t max_row_pixels =
+ MIN(context_surfaces->source_pitch, context_surfaces->dest_pitch) /
+ bytes_per_pixel;
+ size_t row_pixels = MIN(max_row_pixels, image_blit->width);
+
+ hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch +
+ image_blit->width * bytes_per_pixel;
+
+ uint8_t *source_row = source + source_offset;
+ uint8_t *dest_row = dest + dest_offset;
+ size_t row_bytes = row_pixels * bytes_per_pixel;
+
+ size_t adjusted_height = image_blit->height;
+ size_t leftover_bytes = 0;
+
+ hwaddr clipped_dest_size =
+ nv_clip_gpu_tile_blit(d, dest_addr + dest_offset, dest_size);
+
+ if (clipped_dest_size < dest_size) {
+ adjusted_height = clipped_dest_size / context_surfaces->dest_pitch;
+ size_t consumed_bytes = adjusted_height * context_surfaces->dest_pitch;
+
+ leftover_bytes = clipped_dest_size - consumed_bytes;
+ }
+
+ SurfaceBinding *surf_dest = pg->renderer->ops.surface_get(d, dest_addr);
+ if (surf_dest) {
+ if (adjusted_height < surf_dest->height ||
+ row_pixels < surf_dest->width) {
+ pg->renderer->ops.surface_download_if_dirty(d, surf_dest);
+ } else {
+ // The blit will completely replace the surface so any pending
+ // download should be discarded.
+ surf_dest->download_pending = false;
+ surf_dest->draw_dirty = false;
+ }
+ surf_dest->upload_pending = true;
+ pg->draw_time++;
+ }
+
+ NV2A_DPRINTF(" blit 0x%tx -> 0x%tx (Size: %llu, Clipped Height: %zu)\n",
+ source_addr, dest_addr, dest_size, adjusted_height);
+
+ if (adjusted_height > 0) {
+ perform_blit(image_blit->operation, source_row, dest_row, row_pixels,
+ adjusted_height, row_bytes, context_surfaces->source_pitch,
+ context_surfaces->dest_pitch, beta);
+ }
+
+ if (leftover_bytes > 0) {
+ uint8_t *src =
+ source_row + adjusted_height * context_surfaces->source_pitch;
+ uint8_t *dest =
+ dest_row + adjusted_height * context_surfaces->dest_pitch;
+
+ perform_blit(image_blit->operation, src, dest,
+ leftover_bytes / bytes_per_pixel, 1, leftover_bytes,
+ context_surfaces->source_pitch,
+ context_surfaces->dest_pitch, beta);
+ }
+
+ bool needs_alpha_patching;
+ uint8_t alpha_override;
+ switch (context_surfaces->color_format) {
+ case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
+ needs_alpha_patching = true;
+ alpha_override = 0xff;
+ break;
+ case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
+ needs_alpha_patching = true;
+ alpha_override = 0;
+ break;
+ default:
+ needs_alpha_patching = false;
+ alpha_override = 0;
+ }
+
+ if (needs_alpha_patching) {
+ if (adjusted_height > 0) {
+ patch_alpha(dest_row, row_pixels, adjusted_height,
+ context_surfaces->dest_pitch, alpha_override);
+ }
+
+ if (leftover_bytes > 0) {
+ uint8_t *dest =
+ dest_row + adjusted_height * context_surfaces->dest_pitch;
+ patch_alpha(dest, leftover_bytes / 4, 1, 0, alpha_override);
+ }
+}
+
+ dest_addr += dest_offset;
+ memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size,
+ DIRTY_MEMORY_VGA);
+ memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size,
+ DIRTY_MEMORY_NV2A_TEX);
+}
diff --git a/hw/xbox/nv2a/pgraph/gl/meson.build b/hw/xbox/nv2a/pgraph/gl/meson.build
index c19a542adbd..74150a57499 100644
--- a/hw/xbox/nv2a/pgraph/gl/meson.build
+++ b/hw/xbox/nv2a/pgraph/gl/meson.build
@@ -1,5 +1,4 @@
specific_ss.add([sdl, gloffscreen, files(
- 'blit.c',
'debug.c',
'display.c',
'draw.c',
diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.c b/hw/xbox/nv2a/pgraph/gl/renderer.c
index e9e2109779e..f4c675803fc 100644
--- a/hw/xbox/nv2a/pgraph/gl/renderer.c
+++ b/hw/xbox/nv2a/pgraph/gl/renderer.c
@@ -23,6 +23,7 @@
#include "hw/xbox/nv2a/pgraph/pgraph.h"
#include "debug.h"
#include "renderer.h"
+#include "hw/xbox/nv2a/pgraph/surface.h"
GloContext *g_nv2a_context_render;
GloContext *g_nv2a_context_display;
@@ -196,7 +197,6 @@ static PGRAPHRenderer pgraph_gl_renderer = {
.flip_stall = pgraph_gl_flip_stall,
.flush_draw = pgraph_gl_flush_draw,
.get_report = pgraph_gl_get_report,
- .image_blit = pgraph_gl_image_blit,
.pre_savevm_trigger = pgraph_gl_pre_savevm_trigger,
.pre_savevm_wait = pgraph_gl_pre_savevm_wait,
.pre_shutdown_trigger = pgraph_gl_pre_shutdown_trigger,
@@ -204,6 +204,8 @@ static PGRAPHRenderer pgraph_gl_renderer = {
.process_pending = pgraph_gl_process_pending,
.process_pending_reports = pgraph_gl_process_pending_reports,
.surface_update = pgraph_gl_surface_update,
+ .surface_get = pgraph_gl_surface_get,
+ .surface_download_if_dirty = pgraph_gl_surface_download_if_dirty,
.set_surface_scale_factor = pgraph_gl_set_surface_scale_factor,
.get_surface_scale_factor = pgraph_gl_get_surface_scale_factor,
.get_framebuffer_surface = pgraph_gl_get_framebuffer_surface,
diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h
index 9b9d9bfb029..f71901ef7a8 100644
--- a/hw/xbox/nv2a/pgraph/gl/renderer.h
+++ b/hw/xbox/nv2a/pgraph/gl/renderer.h
@@ -256,7 +256,6 @@ void pgraph_gl_draw_begin(NV2AState *d);
void pgraph_gl_draw_end(NV2AState *d);
void pgraph_gl_flush_draw(NV2AState *d);
void pgraph_gl_get_report(NV2AState *d, uint32_t parameter);
-void pgraph_gl_image_blit(NV2AState *d);
void pgraph_gl_mark_textures_possibly_dirty(NV2AState *d, hwaddr addr, hwaddr size);
void pgraph_gl_process_pending_reports(NV2AState *d);
void pgraph_gl_surface_flush(NV2AState *d);
diff --git a/hw/xbox/nv2a/pgraph/meson.build b/hw/xbox/nv2a/pgraph/meson.build
index fc52f609ebd..4a7bdfa3aae 100644
--- a/hw/xbox/nv2a/pgraph/meson.build
+++ b/hw/xbox/nv2a/pgraph/meson.build
@@ -1,4 +1,5 @@
specific_ss.add(files(
+ 'blit.c',
'pgraph.c',
'profile.c',
'rdi.c',
diff --git a/hw/xbox/nv2a/pgraph/pgraph.c b/hw/xbox/nv2a/pgraph/pgraph.c
index b34685866cf..e6acc74811c 100644
--- a/hw/xbox/nv2a/pgraph/pgraph.c
+++ b/hw/xbox/nv2a/pgraph/pgraph.c
@@ -762,7 +762,7 @@ int pgraph_method(NV2AState *d, unsigned int subchannel,
image_blit->height = parameter >> 16;
if (image_blit->width && image_blit->height) {
- d->pgraph.renderer->ops.image_blit(d);
+ pgraph_common_image_blit(d);
}
break;
default:
diff --git a/hw/xbox/nv2a/pgraph/pgraph.h b/hw/xbox/nv2a/pgraph/pgraph.h
index 0449270b55e..85356c623e7 100644
--- a/hw/xbox/nv2a/pgraph/pgraph.h
+++ b/hw/xbox/nv2a/pgraph/pgraph.h
@@ -38,6 +38,7 @@ typedef struct NV2AState NV2AState;
typedef struct PGRAPHNullState PGRAPHNullState;
typedef struct PGRAPHGLState PGRAPHGLState;
typedef struct PGRAPHVkState PGRAPHVkState;
+typedef struct SurfaceBinding SurfaceBinding;
typedef struct VertexAttribute {
bool dma_select;
@@ -119,7 +120,6 @@ typedef struct PGRAPHRenderer {
void (*flip_stall)(NV2AState *d);
void (*flush_draw)(NV2AState *d);
void (*get_report)(NV2AState *d, uint32_t parameter);
- void (*image_blit)(NV2AState *d);
void (*pre_savevm_trigger)(NV2AState *d);
void (*pre_savevm_wait)(NV2AState *d);
void (*pre_shutdown_trigger)(NV2AState *d);
@@ -128,6 +128,8 @@ typedef struct PGRAPHRenderer {
void (*process_pending_reports)(NV2AState *d);
void (*surface_flush)(NV2AState *d);
void (*surface_update)(NV2AState *d, bool upload, bool color_write, bool zeta_write);
+ void *(*surface_get)(NV2AState *d, hwaddr addr);
+ void (*surface_download_if_dirty)(NV2AState *d, void *surface);
void (*set_surface_scale_factor)(NV2AState *d, unsigned int scale);
unsigned int (*get_surface_scale_factor)(NV2AState *d);
int (*get_framebuffer_surface)(NV2AState *d);
diff --git a/hw/xbox/nv2a/pgraph/vk/blit.c b/hw/xbox/nv2a/pgraph/vk/blit.c
deleted file mode 100644
index 9df86bf539c..00000000000
--- a/hw/xbox/nv2a/pgraph/vk/blit.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Geforce NV2A PGRAPH Vulkan Renderer
- *
- * Copyright (c) 2024 Matt Borgerson
- *
- * Based on GL implementation:
- *
- * Copyright (c) 2012 espes
- * Copyright (c) 2015 Jannik Vogel
- * Copyright (c) 2018-2024 Matt Borgerson
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see .
- */
-
-#include "hw/xbox/nv2a/nv2a_int.h"
-#include "renderer.h"
-
-static void perform_blit(int operation, uint8_t *source, uint8_t *dest,
- size_t width, size_t height, size_t width_bytes,
- size_t source_pitch, size_t dest_pitch,
- BetaState *beta)
-{
- if (operation == NV09F_SET_OPERATION_SRCCOPY) {
- for (unsigned int y = 0; y < height; y++) {
- memmove(dest, source, width_bytes);
- source += source_pitch;
- dest += dest_pitch;
- }
- } else if (operation == NV09F_SET_OPERATION_BLEND_AND) {
- uint32_t max_beta_mult = 0x7f80;
- uint32_t beta_mult = beta->beta >> 16;
- uint32_t inv_beta_mult = max_beta_mult - beta_mult;
-
- for (unsigned int y = 0; y < height; y++) {
- uint8_t *s = source;
- uint8_t *d = dest;
- for (unsigned int x = 0; x < width; x++) {
- for (unsigned int ch = 0; ch < 3; ch++) {
- uint32_t a = s[x * 4 + ch] * beta_mult;
- uint32_t b = d[x * 4 + ch] * inv_beta_mult;
- d[x * 4 + ch] = (a + b) / max_beta_mult;
- }
- }
- source += source_pitch;
- dest += dest_pitch;
- }
- } else {
- fprintf(stderr, "Unknown blit operation: 0x%x\n", operation);
- assert(false && "Unknown blit operation");
- }
-}
-
-static void patch_alpha(uint8_t *dest, size_t width_pixels, size_t height,
- size_t dest_pitch, uint8_t alpha_val)
-{
- for (unsigned int y = 0; y < height; y++) {
- uint8_t *d = dest;
- for (unsigned int x = 0; x < width_pixels; x++) {
- d[x * 4 + 3] = alpha_val;
- }
- dest += dest_pitch;
- }
-}
-
-void pgraph_vk_image_blit(NV2AState *d)
-{
- PGRAPHState *pg = &d->pgraph;
- ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d;
- ImageBlitState *image_blit = &pg->image_blit;
- BetaState *beta = &pg->beta;
-
- pgraph_vk_surface_update(d, false, true, true);
-
- assert(context_surfaces->object_instance == image_blit->context_surfaces);
-
- unsigned int bytes_per_pixel;
- switch (context_surfaces->color_format) {
- case NV062_SET_COLOR_FORMAT_LE_Y8:
- bytes_per_pixel = 1;
- break;
- case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
- bytes_per_pixel = 2;
- break;
- case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
- case NV062_SET_COLOR_FORMAT_LE_Y32:
- bytes_per_pixel = 4;
- break;
- default:
- fprintf(stderr, "Unknown blit surface format: 0x%x\n",
- context_surfaces->color_format);
- assert(false);
- break;
- }
-
- hwaddr source_dma_len;
- uint8_t *source = (uint8_t *)nv_dma_map(
- d, context_surfaces->dma_image_source, &source_dma_len);
- assert(context_surfaces->source_offset < source_dma_len);
- source += context_surfaces->source_offset;
- hwaddr source_addr = source - d->vram_ptr;
-
- hwaddr dest_dma_len;
- uint8_t *dest = (uint8_t *)nv_dma_map(d, context_surfaces->dma_image_dest,
- &dest_dma_len);
- assert(context_surfaces->dest_offset < dest_dma_len);
- dest += context_surfaces->dest_offset;
- hwaddr dest_addr = dest - d->vram_ptr;
-
- SurfaceBinding *surf_src = pgraph_vk_surface_get(d, source_addr);
- if (surf_src) {
- pgraph_vk_surface_download_if_dirty(d, surf_src);
- }
-
- hwaddr source_offset = image_blit->in_y * context_surfaces->source_pitch +
- image_blit->in_x * bytes_per_pixel;
- hwaddr dest_offset = image_blit->out_y * context_surfaces->dest_pitch +
- image_blit->out_x * bytes_per_pixel;
-
- size_t max_row_pixels =
- MIN(context_surfaces->source_pitch, context_surfaces->dest_pitch) /
- bytes_per_pixel;
- size_t row_pixels = MIN(max_row_pixels, image_blit->width);
-
- hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch +
- image_blit->width * bytes_per_pixel;
-
- uint8_t *source_row = source + source_offset;
- uint8_t *dest_row = dest + dest_offset;
- size_t row_bytes = row_pixels * bytes_per_pixel;
-
- size_t adjusted_height = image_blit->height;
- size_t leftover_bytes = 0;
-
- hwaddr clipped_dest_size =
- nv_clip_gpu_tile_blit(d, dest_addr + dest_offset, dest_size);
-
- if (clipped_dest_size < dest_size) {
- adjusted_height = clipped_dest_size / context_surfaces->dest_pitch;
- size_t consumed_bytes = adjusted_height * context_surfaces->dest_pitch;
-
- leftover_bytes = clipped_dest_size - consumed_bytes;
- }
-
- SurfaceBinding *surf_dest = pgraph_vk_surface_get(d, dest_addr);
- if (surf_dest) {
- if (adjusted_height < surf_dest->height ||
- row_pixels < surf_dest->width) {
- pgraph_vk_surface_download_if_dirty(d, surf_dest);
- } else {
- // The blit will completely replace the surface so any pending
- // download should be discarded.
- surf_dest->download_pending = false;
- surf_dest->draw_dirty = false;
- }
- surf_dest->upload_pending = true;
- pg->draw_time++;
- }
-
- NV2A_DPRINTF(" blit 0x%tx -> 0x%tx (Size: %llu, Clipped Height: %zu)\n",
- source_addr, dest_addr, dest_size, adjusted_height);
-
- if (adjusted_height > 0) {
- perform_blit(image_blit->operation, source_row, dest_row, row_pixels,
- adjusted_height, row_bytes, context_surfaces->source_pitch,
- context_surfaces->dest_pitch, beta);
- }
-
- if (leftover_bytes > 0) {
- uint8_t *src =
- source_row + adjusted_height * context_surfaces->source_pitch;
- uint8_t *dest =
- dest_row + adjusted_height * context_surfaces->dest_pitch;
-
- perform_blit(image_blit->operation, src, dest,
- leftover_bytes / bytes_per_pixel, 1, leftover_bytes,
- context_surfaces->source_pitch,
- context_surfaces->dest_pitch, beta);
- }
-
- bool needs_alpha_patching;
- uint8_t alpha_override;
- switch (context_surfaces->color_format) {
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
- needs_alpha_patching = true;
- alpha_override = 0xff;
- break;
- case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
- needs_alpha_patching = true;
- alpha_override = 0;
- break;
- default:
- needs_alpha_patching = false;
- alpha_override = 0;
- }
-
- if (needs_alpha_patching) {
- if (adjusted_height > 0) {
- patch_alpha(dest_row, row_pixels, adjusted_height,
- context_surfaces->dest_pitch, alpha_override);
- }
-
- if (leftover_bytes > 0) {
- uint8_t *dest =
- dest_row + adjusted_height * context_surfaces->dest_pitch;
- patch_alpha(dest, leftover_bytes / 4, 1, 0, alpha_override);
- }
- }
-
- dest_addr += dest_offset;
- memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size,
- DIRTY_MEMORY_VGA);
- memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size,
- DIRTY_MEMORY_NV2A_TEX);
-}
diff --git a/hw/xbox/nv2a/pgraph/vk/meson.build b/hw/xbox/nv2a/pgraph/vk/meson.build
index b5d75e2faa4..eaf395f6eba 100644
--- a/hw/xbox/nv2a/pgraph/vk/meson.build
+++ b/hw/xbox/nv2a/pgraph/vk/meson.build
@@ -2,7 +2,6 @@ if vulkan.found()
specific_ss.add([sdl, volk, libglslang, vma, vulkan, spirv_reflect, gloffscreen,
files(
- 'blit.c',
'buffer.c',
'command.c',
'debug.c',
diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.c b/hw/xbox/nv2a/pgraph/vk/renderer.c
index 4272bbceb6d..226b634da07 100644
--- a/hw/xbox/nv2a/pgraph/vk/renderer.c
+++ b/hw/xbox/nv2a/pgraph/vk/renderer.c
@@ -19,7 +19,7 @@
#include "hw/xbox/nv2a/nv2a_int.h"
#include "renderer.h"
-
+#include "hw/xbox/nv2a/pgraph/surface.h"
#include "gloffscreen.h"
#if HAVE_EXTERNAL_MEMORY
@@ -218,7 +218,6 @@ static PGRAPHRenderer pgraph_vk_renderer = {
.flip_stall = pgraph_vk_flip_stall,
.flush_draw = pgraph_vk_flush_draw,
.get_report = pgraph_vk_get_report,
- .image_blit = pgraph_vk_image_blit,
.pre_savevm_trigger = pgraph_vk_pre_savevm_trigger,
.pre_savevm_wait = pgraph_vk_pre_savevm_wait,
.pre_shutdown_trigger = pgraph_vk_pre_shutdown_trigger,
@@ -226,6 +225,8 @@ static PGRAPHRenderer pgraph_vk_renderer = {
.process_pending = pgraph_vk_process_pending,
.process_pending_reports = pgraph_vk_process_pending_reports,
.surface_update = pgraph_vk_surface_update,
+ .surface_get = pgraph_vk_surface_get,
+ .surface_download_if_dirty = pgraph_vk_surface_download_if_dirty,
.set_surface_scale_factor = pgraph_vk_set_surface_scale_factor,
.get_surface_scale_factor = pgraph_vk_get_surface_scale_factor,
.get_framebuffer_surface = pgraph_vk_get_framebuffer_surface,
diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h
index 8cf9e4d6638..9295fa43e00 100644
--- a/hw/xbox/nv2a/pgraph/vk/renderer.h
+++ b/hw/xbox/nv2a/pgraph/vk/renderer.h
@@ -591,8 +591,6 @@ void pgraph_vk_ensure_not_in_render_pass(PGRAPHState *pg);
VkCommandBuffer pgraph_vk_begin_nondraw_commands(PGRAPHState *pg);
void pgraph_vk_end_nondraw_commands(PGRAPHState *pg, VkCommandBuffer cmd);
-// blit.c
-void pgraph_vk_image_blit(NV2AState *d);
// gpuprops.c
void pgraph_vk_determine_gpu_properties(NV2AState *d);