Skip to content

Commit b7aad53

Browse files
nv2a/pgraph: refactor blit into shared implementation with GL/VK wrappers
1 parent be882c0 commit b7aad53

File tree

4 files changed

+264
-351
lines changed

4 files changed

+264
-351
lines changed

hw/xbox/nv2a/pgraph/blit.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Geforce NV2A PGRAPH Renderer
3+
*
4+
* Copyright (c) 2012 espes
5+
* Copyright (c) 2015 Jannik Vogel
6+
* Copyright (c) 2018-2024 Matt Borgerson
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; either
11+
* version 2 of the License, or (at your option) any later version.
12+
*
13+
* This library is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
* Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public
19+
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
#include "hw/xbox/nv2a/nv2a_int.h"
23+
#include "hw/xbox/nv2a/pgraph/blit.h"
24+
25+
26+
// TODO: Optimize. Ideally this should all be done via OpenGL.
27+
void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops)
28+
29+
{
30+
PGRAPHState *pg = &d->pgraph;
31+
ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d;
32+
ImageBlitState *image_blit = &pg->image_blit;
33+
BetaState *beta = &pg->beta;
34+
35+
ops->surface_update(d, false, true, true);
36+
37+
assert(context_surfaces->object_instance == image_blit->context_surfaces);
38+
39+
unsigned int bytes_per_pixel;
40+
switch (context_surfaces->color_format) {
41+
case NV062_SET_COLOR_FORMAT_LE_Y8:
42+
bytes_per_pixel = 1;
43+
break;
44+
case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
45+
bytes_per_pixel = 2;
46+
break;
47+
case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
48+
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
49+
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
50+
case NV062_SET_COLOR_FORMAT_LE_Y32:
51+
bytes_per_pixel = 4;
52+
break;
53+
default:
54+
fprintf(stderr, "Unknown blit surface format: 0x%x\n",
55+
context_surfaces->color_format);
56+
assert(false);
57+
break;
58+
}
59+
60+
hwaddr source_dma_len, dest_dma_len;
61+
62+
uint8_t *source = (uint8_t *)nv_dma_map(
63+
d, context_surfaces->dma_image_source, &source_dma_len);
64+
assert(context_surfaces->source_offset < source_dma_len);
65+
source += context_surfaces->source_offset;
66+
67+
uint8_t *dest = (uint8_t *)nv_dma_map(d, context_surfaces->dma_image_dest,
68+
&dest_dma_len);
69+
assert(context_surfaces->dest_offset < dest_dma_len);
70+
dest += context_surfaces->dest_offset;
71+
72+
hwaddr source_addr = source - d->vram_ptr;
73+
hwaddr dest_addr = dest - d->vram_ptr;
74+
75+
SurfaceBinding *surf_src = ops->surface_get(d, source_addr);
76+
if (surf_src) {
77+
ops->surface_download_if_dirty(d, surf_src);
78+
}
79+
80+
SurfaceBinding *surf_dest = ops->surface_get(d, dest_addr);
81+
if (surf_dest) {
82+
if (image_blit->height < surf_dest->height ||
83+
image_blit->width < surf_dest->width) {
84+
ops->surface_download_if_dirty(d, surf_dest);
85+
} else {
86+
// The blit will completely replace the surface so any pending
87+
// download should be discarded.
88+
surf_dest->download_pending = false;
89+
surf_dest->draw_dirty = false;
90+
}
91+
surf_dest->upload_pending = true;
92+
pg->draw_time++;
93+
}
94+
95+
hwaddr source_offset = image_blit->in_y * context_surfaces->source_pitch +
96+
image_blit->in_x * bytes_per_pixel;
97+
hwaddr dest_offset = image_blit->out_y * context_surfaces->dest_pitch +
98+
image_blit->out_x * bytes_per_pixel;
99+
100+
hwaddr source_size =
101+
(image_blit->height - 1) * context_surfaces->source_pitch +
102+
image_blit->width * bytes_per_pixel;
103+
hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch +
104+
image_blit->width * bytes_per_pixel;
105+
106+
/* FIXME: What does hardware do in this case? */
107+
assert(source_addr + source_offset + source_size <=
108+
memory_region_size(d->vram));
109+
assert(dest_addr + dest_offset + dest_size <= memory_region_size(d->vram));
110+
111+
uint8_t *source_row = source + source_offset;
112+
uint8_t *dest_row = dest + dest_offset;
113+
114+
if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) {
115+
// NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_SRCCOPY");
116+
for (unsigned int y = 0; y < image_blit->height; y++) {
117+
memmove(dest_row, source_row, image_blit->width * bytes_per_pixel);
118+
source_row += context_surfaces->source_pitch;
119+
dest_row += context_surfaces->dest_pitch;
120+
}
121+
} else if (image_blit->operation == NV09F_SET_OPERATION_BLEND_AND) {
122+
// NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_BLEND_AND");
123+
uint32_t max_beta_mult = 0x7f80;
124+
uint32_t beta_mult = beta->beta >> 16;
125+
uint32_t inv_beta_mult = max_beta_mult - beta_mult;
126+
for (unsigned int y = 0; y < image_blit->height; y++) {
127+
for (unsigned int x = 0; x < image_blit->width; x++) {
128+
for (unsigned int ch = 0; ch < 3; ch++) {
129+
uint32_t a = source_row[x * 4 + ch] * beta_mult;
130+
uint32_t b = dest_row[x * 4 + ch] * inv_beta_mult;
131+
dest_row[x * 4 + ch] = (a + b) / max_beta_mult;
132+
}
133+
}
134+
source_row += context_surfaces->source_pitch;
135+
dest_row += context_surfaces->dest_pitch;
136+
}
137+
} else {
138+
fprintf(stderr, "Unknown blit operation: 0x%x\n",
139+
image_blit->operation);
140+
assert(false && "Unknown blit operation");
141+
}
142+
143+
NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source_addr, dest_addr);
144+
145+
bool needs_alpha_patching;
146+
uint8_t alpha_override;
147+
switch (context_surfaces->color_format) {
148+
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
149+
needs_alpha_patching = true;
150+
alpha_override = 0xff;
151+
break;
152+
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
153+
needs_alpha_patching = true;
154+
alpha_override = 0;
155+
break;
156+
default:
157+
needs_alpha_patching = false;
158+
alpha_override = 0;
159+
}
160+
161+
if (needs_alpha_patching) {
162+
dest_row = dest + dest_offset;
163+
for (unsigned int y = 0; y < image_blit->height; y++) {
164+
for (unsigned int x = 0; x < image_blit->width; x++) {
165+
dest_row[x * 4 + 3] = alpha_override;
166+
}
167+
dest_row += context_surfaces->dest_pitch;
168+
}
169+
}
170+
171+
dest_addr += dest_offset;
172+
memory_region_set_client_dirty(d->vram, dest_addr, dest_size,
173+
DIRTY_MEMORY_VGA);
174+
memory_region_set_client_dirty(d->vram, dest_addr, dest_size,
175+
DIRTY_MEMORY_NV2A_TEX);
176+
}

hw/xbox/nv2a/pgraph/blit.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Header for blit.c
2+
3+
#pragma once
4+
5+
#include "hw/xbox/nv2a/nv2a_int.h" // NV2AState, hwaddr, SurfaceBinding
6+
7+
typedef struct PGRAPHSurfaceOps {
8+
void (*surface_update)(NV2AState *d, bool, bool, bool);
9+
SurfaceBinding *(*surface_get)(NV2AState *d, hwaddr addr);
10+
void (*surface_download_if_dirty)(NV2AState *d, SurfaceBinding *surf);
11+
} PGRAPHSurfaceOps;
12+
13+
void pgraph_common_image_blit(NV2AState *d, const PGRAPHSurfaceOps *ops);

0 commit comments

Comments
 (0)