mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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.
This commit is contained in:
@@ -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, \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
+127
-78
@@ -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);
|
||||
}
|
||||
|
||||
+127
-77
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user