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); }