From f74e009ba95912a3744126973e81eb7731c33173 Mon Sep 17 00:00:00 2001 From: Trenton Turner Date: Thu, 15 Jan 2026 19:04:30 -0600 Subject: [PATCH 1/3] nv2a/pgraph: refactor blit into shared implementation with GL/VK wrappers --- hw/xbox/nv2a/pgraph/blit.c | 176 +++++++++++++++++++++++++++++ hw/xbox/nv2a/pgraph/blit.h | 13 +++ hw/xbox/nv2a/pgraph/gl/blit.c | 203 ++-------------------------------- hw/xbox/nv2a/pgraph/vk/blit.c | 202 ++------------------------------- 4 files changed, 204 insertions(+), 390 deletions(-) create mode 100644 hw/xbox/nv2a/pgraph/blit.c create mode 100644 hw/xbox/nv2a/pgraph/blit.h diff --git a/hw/xbox/nv2a/pgraph/blit.c b/hw/xbox/nv2a/pgraph/blit.c new file mode 100644 index 00000000000..9b18c5ba5e8 --- /dev/null +++ b/hw/xbox/nv2a/pgraph/blit.c @@ -0,0 +1,176 @@ +/* + * 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" +#include "hw/xbox/nv2a/pgraph/blit.h" + + +// TODO: Optimize. Ideally this should all be done via OpenGL. +void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops) + +{ + 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); + + 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 = ops->surface_get(d, source_addr); + if (surf_src) { + ops->surface_download_if_dirty(d, surf_src); + } + + SurfaceBinding *surf_dest = ops->surface_get(d, dest_addr); + if (surf_dest) { + if (image_blit->height < surf_dest->height || + image_blit->width < surf_dest->width) { + 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++; + } + + 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; + + hwaddr source_size = + (image_blit->height - 1) * context_surfaces->source_pitch + + image_blit->width * bytes_per_pixel; + hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch + + image_blit->width * bytes_per_pixel; + + /* FIXME: What does hardware do in this case? */ + assert(source_addr + source_offset + source_size <= + memory_region_size(d->vram)); + assert(dest_addr + dest_offset + dest_size <= memory_region_size(d->vram)); + + uint8_t *source_row = source + source_offset; + uint8_t *dest_row = dest + dest_offset; + + if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { + // NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_SRCCOPY"); + for (unsigned int y = 0; y < image_blit->height; y++) { + memmove(dest_row, source_row, image_blit->width * bytes_per_pixel); + source_row += context_surfaces->source_pitch; + 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"); + 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 < image_blit->height; y++) { + for (unsigned int x = 0; x < image_blit->width; x++) { + for (unsigned int ch = 0; ch < 3; ch++) { + uint32_t a = source_row[x * 4 + ch] * beta_mult; + uint32_t b = dest_row[x * 4 + ch] * inv_beta_mult; + dest_row[x * 4 + ch] = (a + b) / max_beta_mult; + } + } + source_row += context_surfaces->source_pitch; + dest_row += context_surfaces->dest_pitch; + } + } else { + fprintf(stderr, "Unknown blit operation: 0x%x\n", + image_blit->operation); + assert(false && "Unknown blit operation"); + } + + NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source_addr, dest_addr); + + 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) { + dest_row = dest + dest_offset; + for (unsigned int y = 0; y < image_blit->height; y++) { + for (unsigned int x = 0; x < image_blit->width; x++) { + dest_row[x * 4 + 3] = alpha_override; + } + dest_row += context_surfaces->dest_pitch; + } + } + + dest_addr += dest_offset; + memory_region_set_client_dirty(d->vram, dest_addr, dest_size, + DIRTY_MEMORY_VGA); + memory_region_set_client_dirty(d->vram, dest_addr, dest_size, + DIRTY_MEMORY_NV2A_TEX); +} diff --git a/hw/xbox/nv2a/pgraph/blit.h b/hw/xbox/nv2a/pgraph/blit.h new file mode 100644 index 00000000000..0c75543d01e --- /dev/null +++ b/hw/xbox/nv2a/pgraph/blit.h @@ -0,0 +1,13 @@ +// Header for blit.c + +#pragma once + +#include "hw/xbox/nv2a/nv2a_int.h" // NV2AState, hwaddr, SurfaceBinding + +typedef struct PGRAPHSurfaceOps { + void (*surface_update)(NV2AState *d, bool, bool, bool); + SurfaceBinding *(*surface_get)(NV2AState *d, hwaddr addr); + void (*surface_download_if_dirty)(NV2AState *d, SurfaceBinding *surf); +} PGRAPHSurfaceOps; + +void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops); diff --git a/hw/xbox/nv2a/pgraph/gl/blit.c b/hw/xbox/nv2a/pgraph/gl/blit.c index 6850ffb7c7d..747a164814f 100644 --- a/hw/xbox/nv2a/pgraph/gl/blit.c +++ b/hw/xbox/nv2a/pgraph/gl/blit.c @@ -21,203 +21,16 @@ #include "hw/xbox/nv2a/nv2a_int.h" #include "renderer.h" +#include "hw/xbox/nv2a/pgraph/blit.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; - } -} - +// TODO: Optimize. 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); - } - } + static const PGRAPHSurfaceOps ops = { + .surface_update = pgraph_gl_surface_update, + .surface_get = pgraph_gl_surface_get, + .surface_download_if_dirty = pgraph_gl_surface_download_if_dirty, + }; - 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); + pgraph_common_image_blit(d, &ops); } diff --git a/hw/xbox/nv2a/pgraph/vk/blit.c b/hw/xbox/nv2a/pgraph/vk/blit.c index 9df86bf539c..00c8724f3a6 100644 --- a/hw/xbox/nv2a/pgraph/vk/blit.c +++ b/hw/xbox/nv2a/pgraph/vk/blit.c @@ -25,203 +25,15 @@ #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; - } -} +#include "hw/xbox/nv2a/pgraph/blit.h" 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); - } - } + static const PGRAPHSurfaceOps ops = { + .surface_update = pgraph_vk_surface_update, + .surface_get = pgraph_vk_surface_get, + .surface_download_if_dirty = pgraph_vk_surface_download_if_dirty, + }; - 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); + pgraph_common_image_blit(d, &ops); } From 7176193f20d786e2696ae38baeb220f58016795a Mon Sep 17 00:00:00 2001 From: KraftMacAndChee Date: Fri, 16 Jan 2026 14:08:27 -0600 Subject: [PATCH 2/3] nv2a/pgraph: remove renderer image_blit op and use shared implementation Used a shared implementation and dropped gl/blit.c. vk/blit.c, and blit.h --- hw/xbox/nv2a/pgraph/blit.c | 14 +++++------ hw/xbox/nv2a/pgraph/blit.h | 13 ---------- hw/xbox/nv2a/pgraph/gl/blit.c | 36 --------------------------- hw/xbox/nv2a/pgraph/gl/meson.build | 1 - hw/xbox/nv2a/pgraph/gl/renderer.c | 4 ++- hw/xbox/nv2a/pgraph/gl/renderer.h | 1 - hw/xbox/nv2a/pgraph/meson.build | 1 + hw/xbox/nv2a/pgraph/pgraph.c | 2 +- hw/xbox/nv2a/pgraph/pgraph.h | 5 +++- hw/xbox/nv2a/pgraph/vk/blit.c | 39 ------------------------------ hw/xbox/nv2a/pgraph/vk/meson.build | 1 - hw/xbox/nv2a/pgraph/vk/renderer.c | 5 ++-- hw/xbox/nv2a/pgraph/vk/renderer.h | 2 -- 13 files changed, 19 insertions(+), 105 deletions(-) delete mode 100644 hw/xbox/nv2a/pgraph/blit.h delete mode 100644 hw/xbox/nv2a/pgraph/gl/blit.c delete mode 100644 hw/xbox/nv2a/pgraph/vk/blit.c diff --git a/hw/xbox/nv2a/pgraph/blit.c b/hw/xbox/nv2a/pgraph/blit.c index 9b18c5ba5e8..317c94cf649 100644 --- a/hw/xbox/nv2a/pgraph/blit.c +++ b/hw/xbox/nv2a/pgraph/blit.c @@ -23,8 +23,8 @@ #include "hw/xbox/nv2a/pgraph/blit.h" -// TODO: Optimize. Ideally this should all be done via OpenGL. -void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops) + +void pgraph_common_image_blit(NV2AState *d) { PGRAPHState *pg = &d->pgraph; @@ -32,7 +32,7 @@ void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops) ImageBlitState *image_blit = &pg->image_blit; BetaState *beta = &pg->beta; - ops->surface_update(d, false, true, true); + pg->renderer->ops.surface_update(d, false, true, true); assert(context_surfaces->object_instance == image_blit->context_surfaces); @@ -72,16 +72,16 @@ void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops) hwaddr source_addr = source - d->vram_ptr; hwaddr dest_addr = dest - d->vram_ptr; - SurfaceBinding *surf_src = ops->surface_get(d, source_addr); + SurfaceBinding *surf_src = pg->renderer->ops.surface_get(d, source_addr); if (surf_src) { - ops->surface_download_if_dirty(d, surf_src); + pg->renderer->ops.surface_download_if_dirty(d, surf_src); } - SurfaceBinding *surf_dest = ops->surface_get(d, dest_addr); + SurfaceBinding *surf_dest = pg->renderer->ops.surface_get(d, dest_addr); if (surf_dest) { if (image_blit->height < surf_dest->height || image_blit->width < surf_dest->width) { - ops->surface_download_if_dirty(d, surf_dest); + 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. diff --git a/hw/xbox/nv2a/pgraph/blit.h b/hw/xbox/nv2a/pgraph/blit.h deleted file mode 100644 index 0c75543d01e..00000000000 --- a/hw/xbox/nv2a/pgraph/blit.h +++ /dev/null @@ -1,13 +0,0 @@ -// Header for blit.c - -#pragma once - -#include "hw/xbox/nv2a/nv2a_int.h" // NV2AState, hwaddr, SurfaceBinding - -typedef struct PGRAPHSurfaceOps { - void (*surface_update)(NV2AState *d, bool, bool, bool); - SurfaceBinding *(*surface_get)(NV2AState *d, hwaddr addr); - void (*surface_download_if_dirty)(NV2AState *d, SurfaceBinding *surf); -} PGRAPHSurfaceOps; - -void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops); diff --git a/hw/xbox/nv2a/pgraph/gl/blit.c b/hw/xbox/nv2a/pgraph/gl/blit.c deleted file mode 100644 index 747a164814f..00000000000 --- a/hw/xbox/nv2a/pgraph/gl/blit.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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" -#include "hw/xbox/nv2a/pgraph/blit.h" - -// TODO: Optimize. -void pgraph_gl_image_blit(NV2AState *d) -{ - static const PGRAPHSurfaceOps ops = { - .surface_update = pgraph_gl_surface_update, - .surface_get = pgraph_gl_surface_get, - .surface_download_if_dirty = pgraph_gl_surface_download_if_dirty, - }; - - pgraph_common_image_blit(d, &ops); -} 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..b0f97dcb667 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.h +++ b/hw/xbox/nv2a/pgraph/pgraph.h @@ -38,6 +38,8 @@ 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 +121,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 +129,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 00c8724f3a6..00000000000 --- a/hw/xbox/nv2a/pgraph/vk/blit.c +++ /dev/null @@ -1,39 +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" -#include "hw/xbox/nv2a/pgraph/blit.h" - -void pgraph_vk_image_blit(NV2AState *d) -{ - static const PGRAPHSurfaceOps ops = { - .surface_update = pgraph_vk_surface_update, - .surface_get = pgraph_vk_surface_get, - .surface_download_if_dirty = pgraph_vk_surface_download_if_dirty, - }; - - pgraph_common_image_blit(d, &ops); -} 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); From abc660617e6f51a7ec5a40b1c828615c465d991c Mon Sep 17 00:00:00 2001 From: KraftMacAndChee Date: Fri, 16 Jan 2026 19:56:19 -0600 Subject: [PATCH 3/3] nv2a/pgraph: fixed image blit regression after refactor --- hw/xbox/nv2a/pgraph/blit.c | 161 ++++++++++++++++++++++------------- hw/xbox/nv2a/pgraph/pgraph.h | 1 - 2 files changed, 104 insertions(+), 58 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/blit.c b/hw/xbox/nv2a/pgraph/blit.c index 317c94cf649..ca5ab1d9289 100644 --- a/hw/xbox/nv2a/pgraph/blit.c +++ b/hw/xbox/nv2a/pgraph/blit.c @@ -20,10 +20,53 @@ */ #include "hw/xbox/nv2a/nv2a_int.h" -#include "hw/xbox/nv2a/pgraph/blit.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) { @@ -77,10 +120,40 @@ void pgraph_common_image_blit(NV2AState *d) 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 (image_blit->height < surf_dest->height || - image_blit->width < surf_dest->width) { + 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 @@ -92,55 +165,26 @@ void pgraph_common_image_blit(NV2AState *d) pg->draw_time++; } - 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; + NV2A_DPRINTF(" blit 0x%tx -> 0x%tx (Size: %llu, Clipped Height: %zu)\n", + source_addr, dest_addr, dest_size, adjusted_height); - hwaddr source_size = - (image_blit->height - 1) * context_surfaces->source_pitch + - image_blit->width * bytes_per_pixel; - hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch + - image_blit->width * bytes_per_pixel; + 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); + } - /* FIXME: What does hardware do in this case? */ - assert(source_addr + source_offset + source_size <= - memory_region_size(d->vram)); - assert(dest_addr + dest_offset + dest_size <= memory_region_size(d->vram)); + 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; - uint8_t *source_row = source + source_offset; - uint8_t *dest_row = dest + dest_offset; - - if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { - // NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_SRCCOPY"); - for (unsigned int y = 0; y < image_blit->height; y++) { - memmove(dest_row, source_row, image_blit->width * bytes_per_pixel); - source_row += context_surfaces->source_pitch; - 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"); - 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 < image_blit->height; y++) { - for (unsigned int x = 0; x < image_blit->width; x++) { - for (unsigned int ch = 0; ch < 3; ch++) { - uint32_t a = source_row[x * 4 + ch] * beta_mult; - uint32_t b = dest_row[x * 4 + ch] * inv_beta_mult; - dest_row[x * 4 + ch] = (a + b) / max_beta_mult; - } - } - source_row += context_surfaces->source_pitch; - dest_row += 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); } - } else { - fprintf(stderr, "Unknown blit operation: 0x%x\n", - image_blit->operation); - assert(false && "Unknown blit operation"); - } - - NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source_addr, dest_addr); bool needs_alpha_patching; uint8_t alpha_override; @@ -159,18 +203,21 @@ void pgraph_common_image_blit(NV2AState *d) } if (needs_alpha_patching) { - dest_row = dest + dest_offset; - for (unsigned int y = 0; y < image_blit->height; y++) { - for (unsigned int x = 0; x < image_blit->width; x++) { - dest_row[x * 4 + 3] = alpha_override; - } - dest_row += context_surfaces->dest_pitch; - } + 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, dest_size, + memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size, DIRTY_MEMORY_VGA); - memory_region_set_client_dirty(d->vram, dest_addr, dest_size, + memory_region_set_client_dirty(d->vram, dest_addr, clipped_dest_size, DIRTY_MEMORY_NV2A_TEX); } diff --git a/hw/xbox/nv2a/pgraph/pgraph.h b/hw/xbox/nv2a/pgraph/pgraph.h index b0f97dcb667..85356c623e7 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.h +++ b/hw/xbox/nv2a/pgraph/pgraph.h @@ -40,7 +40,6 @@ typedef struct PGRAPHGLState PGRAPHGLState; typedef struct PGRAPHVkState PGRAPHVkState; typedef struct SurfaceBinding SurfaceBinding; - typedef struct VertexAttribute { bool dma_select; hwaddr offset;