From 24087af22ab9df22a0fa214188266430c3cf1387 Mon Sep 17 00:00:00 2001 From: Erik Abair Date: Thu, 15 Jan 2026 13:04:14 -0800 Subject: [PATCH] nv2a: Clip image blits to GPU tile bounds Hardware blits that overlap an active GPU tile are clipped to the bounds of that tile. This change checks blits against registered PFB tiles (which are mirrored to PGRAPH via RDI) and clips blits as needed. --- hw/xbox/nv2a/nv2a.c | 21 ++++ hw/xbox/nv2a/nv2a_int.h | 8 ++ hw/xbox/nv2a/nv2a_regs.h | 9 ++ hw/xbox/nv2a/pgraph/gl/blit.c | 205 +++++++++++++++++++++------------- hw/xbox/nv2a/pgraph/vk/blit.c | 204 ++++++++++++++++++++------------- 5 files changed, 292 insertions(+), 155 deletions(-) diff --git a/hw/xbox/nv2a/nv2a.c b/hw/xbox/nv2a/nv2a.c index 02cfbca3e9..6212687360 100644 --- a/hw/xbox/nv2a/nv2a.c +++ b/hw/xbox/nv2a/nv2a.c @@ -85,6 +85,27 @@ void *nv_dma_map(NV2AState *d, hwaddr dma_obj_address, hwaddr *len) return d->vram_ptr + dma.address; } +hwaddr nv_clip_gpu_tile_blit(NV2AState *d, hwaddr blit_base_address, hwaddr len) +{ + const uint32_t *regs = d->pfb.regs; + hwaddr blit_end = blit_base_address + len; + for (int i = 0; i < NV_NUM_GPU_TILES; ++i) { + uint32_t base_and_flags = regs[NV_PFB_TILE_BASE_ADDRESS_AND_FLAGS(i)]; + if (!(base_and_flags & NV_PFB_TILE_FLAGS_VALID)) { + continue; + } + + uint32_t limit = regs[NV_PFB_TILE_LIMIT(i)]; + + if (blit_base_address < limit && blit_end > limit) { + // TODO: Determine HW behavior if tiles are consecutive. + return limit + 1 - blit_base_address; + } + } + + return len; +} + const NV2ABlockInfo blocktable[NV_NUM_BLOCKS] = { #define ENTRY(NAME, LNAME, OFFSET, SIZE) [NV_##NAME] = { \ .name = #NAME, \ diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h index e007c287b2..917f4ffe6e 100644 --- a/hw/xbox/nv2a/nv2a_int.h +++ b/hw/xbox/nv2a/nv2a_int.h @@ -210,5 +210,13 @@ DEFINE_PROTO(user) DMAObject nv_dma_load(NV2AState *d, hwaddr dma_obj_address); void *nv_dma_map(NV2AState *d, hwaddr dma_obj_address, hwaddr *len); +/** + * Clips an image blit to fit into a GPU tile it overlaps. + * @param blit_base_address Address of the blit target + * @param len Length of the blit in bytes + * @return The adjusted length + */ +hwaddr nv_clip_gpu_tile_blit(NV2AState *d, hwaddr blit_base_address, + hwaddr len); #endif diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 65f4e9d11a..6bfd6e7d8a 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -57,6 +57,8 @@ #define NV_PRAMIN 19 /* RAMIN access */ #define NV_USER 20 /* PFIFO MMIO and DMA submission area */ +#define NV_NUM_GPU_TILES 8 + #define NV_PMC_BOOT_0 0x00000000 #define NV_PMC_INTR_0 0x00000100 # define NV_PMC_INTR_0_PFIFO (1 << 8) @@ -716,6 +718,13 @@ #define NV_PFB_CFG0 0x00000200 # define NV_PFB_CFG0_PART 0x00000003 #define NV_PFB_CSTATUS 0x0000020C +#define NV_PFB_TILE 0x00000240 +# define NV_PFB_TILE_BASE_ADDRESS_AND_FLAGS(i) (NV_PFB_TILE + (i) * 16) +# define NV_PFB_TILE_FLAGS 0x00003FFF +# define NV_PFB_TILE_FLAGS_VALID 1 +# define NV_PFB_TILE_BASE_ADDRESS 0x03FFC000 +# define NV_PFB_TILE_LIMIT(i) (NV_PFB_TILE + (i) * 16 + 4) +# define NV_PFB_TILE_PITCH(i) (NV_PFB_TILE + (i) * 16 + 8) #define NV_PFB_WBC 0x00000410 # define NV_PFB_WBC_FLUSH (1 << 16) diff --git a/hw/xbox/nv2a/pgraph/gl/blit.c b/hw/xbox/nv2a/pgraph/gl/blit.c index b4cce8a5ef..6850ffb7c7 100644 --- a/hw/xbox/nv2a/pgraph/gl/blit.c +++ b/hw/xbox/nv2a/pgraph/gl/blit.c @@ -22,7 +22,53 @@ #include "hw/xbox/nv2a/nv2a_int.h" #include "renderer.h" -// TODO: Optimize. Ideally this should all be done via OpenGL. +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; @@ -36,38 +82,37 @@ void pgraph_gl_image_blit(NV2AState *d) 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; + 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; - + 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 source_addr = source - d->vram_ptr; hwaddr dest_addr = dest - d->vram_ptr; SurfaceBinding *surf_src = pgraph_gl_surface_get(d, source_addr); @@ -75,10 +120,40 @@ void pgraph_gl_image_blit(NV2AState *d) 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 (image_blit->height < surf_dest->height || - image_blit->width < surf_dest->width) { + 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 @@ -90,55 +165,26 @@ void pgraph_gl_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; - - /* 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"); + 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); } - NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source_addr, dest_addr); + 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; @@ -157,18 +203,21 @@ void pgraph_gl_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/vk/blit.c b/hw/xbox/nv2a/pgraph/vk/blit.c index e4529a3c58..9df86bf539 100644 --- a/hw/xbox/nv2a/pgraph/vk/blit.c +++ b/hw/xbox/nv2a/pgraph/vk/blit.c @@ -26,6 +26,53 @@ #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; @@ -39,38 +86,37 @@ void pgraph_vk_image_blit(NV2AState *d) 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; + 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; - + 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 source_addr = source - d->vram_ptr; hwaddr dest_addr = dest - d->vram_ptr; SurfaceBinding *surf_src = pgraph_vk_surface_get(d, source_addr); @@ -78,10 +124,40 @@ void pgraph_vk_image_blit(NV2AState *d) 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 (image_blit->height < surf_dest->height || - image_blit->width < surf_dest->width) { + 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 @@ -93,55 +169,26 @@ void pgraph_vk_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; - - /* 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"); + 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); } - NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source_addr, dest_addr); + 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; @@ -160,18 +207,21 @@ void pgraph_vk_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); }