nv2a: Implement SET_LINE_WIDTH

This commit is contained in:
Matt Borgerson 2025-06-22 16:40:17 -07:00
parent a242964793
commit 58fcee1f12
8 changed files with 70 additions and 16 deletions

View File

@ -503,6 +503,7 @@
# define NV_PGRAPH_SETUPRASTER_POINTSMOOTHENABLE (1 << 9)
# define NV_PGRAPH_SETUPRASTER_LINESMOOTHENABLE (1 << 10)
# define NV_PGRAPH_SETUPRASTER_POLYSMOOTHENABLE (1 << 11)
# define NV_PGRAPH_SETUPRASTER_LINEWIDTH 0x001ff000
# define NV_PGRAPH_SETUPRASTER_CULLCTRL 0x00600000
# define NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT 1
# define NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK 2
@ -1009,6 +1010,7 @@
# define NV097_SET_SHADE_MODE 0x0000037C
# define NV097_SET_SHADE_MODE_V_FLAT 0x1D00
# define NV097_SET_SHADE_MODE_V_SMOOTH 0x1D01
# define NV097_SET_LINE_WIDTH 0x00000380
# define NV097_SET_POLYGON_OFFSET_SCALE_FACTOR 0x00000384
# define NV097_SET_POLYGON_OFFSET_BIAS 0x00000388
# define NV097_SET_FRONT_POLYGON_MODE 0x0000038C

View File

@ -23,6 +23,7 @@
#include "hw/xbox/nv2a/nv2a_int.h"
#include "debug.h"
#include "renderer.h"
#include <math.h>
void pgraph_gl_clear_surface(NV2AState *d, uint32_t parameter)
{
@ -131,6 +132,32 @@ void pgraph_gl_clear_surface(NV2AState *d, uint32_t parameter)
pg->clearing = false;
}
static float clamp_line_width_to_device_limits(PGRAPHState *pg, float width,
bool smooth)
{
PGRAPHGLState *r = pg->gl_renderer_state;
float min_width;
float max_width;
float granularity;
if (smooth) {
min_width = r->limits.smooth_line_width.range[0];
max_width = r->limits.smooth_line_width.range[1];
granularity = r->limits.smooth_line_width.granularity;
} else {
min_width = r->limits.aliased_line_width.range[0];
max_width = r->limits.aliased_line_width.range[1];
granularity = r->limits.aliased_line_width.granularity;
}
if (granularity != 0.0f) {
float steps = roundf((width - min_width) / granularity);
width = min_width + steps * granularity;
}
return fminf(fmaxf(min_width, width), max_width);
}
void pgraph_gl_draw_begin(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
@ -310,13 +337,14 @@ void pgraph_gl_draw_begin(NV2AState *d)
bool anti_aliasing = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_ANTIALIASING), NV_PGRAPH_ANTIALIASING_ENABLE);
/* Edge Antialiasing */
float line_width = pgraph_get_line_width(pg) * pg->surface_scale_factor;
if (!anti_aliasing && pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
NV_PGRAPH_SETUPRASTER_LINESMOOTHENABLE) {
glEnable(GL_LINE_SMOOTH);
glLineWidth(MIN(r->supported_smooth_line_width_range[1], pg->surface_scale_factor));
glLineWidth(clamp_line_width_to_device_limits(pg, line_width, true));
} else {
glDisable(GL_LINE_SMOOTH);
glLineWidth(MIN(r->supported_aliased_line_width_range[1], pg->surface_scale_factor));
glLineWidth(clamp_line_width_to_device_limits(pg, line_width, false));
}
if (!anti_aliasing && pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
NV_PGRAPH_SETUPRASTER_POLYSMOOTHENABLE) {

View File

@ -52,8 +52,12 @@ static void pgraph_gl_init(NV2AState *d, Error **errp)
/* Internal RGB565 texture format */
assert(glo_check_extension("GL_ARB_ES2_compatibility"));
glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, r->supported_smooth_line_width_range);
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, r->supported_aliased_line_width_range);
glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, r->limits.smooth_line_width.range);
glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY,
&r->limits.smooth_line_width.granularity);
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE,
r->limits.aliased_line_width.range);
r->limits.aliased_line_width.granularity = 1.0;
pgraph_gl_init_surfaces(pg);
pgraph_gl_init_reports(d);

View File

@ -234,8 +234,12 @@ typedef struct PGRAPHGLState {
GLint palette_loc[256];
} disp_rndr;
GLfloat supported_aliased_line_width_range[2];
GLfloat supported_smooth_line_width_range[2];
struct {
struct {
float range[2];
float granularity;
} smooth_line_width, aliased_line_width;
} limits;
} PGRAPHGLState;
extern GloContext *g_nv2a_context_render;

View File

@ -68,6 +68,7 @@ DEF_METHOD(NV097, SET_STENCIL_OP_FAIL)
DEF_METHOD(NV097, SET_STENCIL_OP_ZFAIL)
DEF_METHOD(NV097, SET_STENCIL_OP_ZPASS)
DEF_METHOD(NV097, SET_SHADE_MODE)
DEF_METHOD(NV097, SET_LINE_WIDTH)
DEF_METHOD(NV097, SET_POLYGON_OFFSET_SCALE_FACTOR)
DEF_METHOD(NV097, SET_POLYGON_OFFSET_BIAS)
DEF_METHOD(NV097, SET_FRONT_POLYGON_MODE)

View File

@ -28,15 +28,6 @@
#include "swizzle.h"
#include "nv2a_vsh_emulator.h"
#define PG_GET_MASK(reg, mask) GET_MASK(pgraph_reg_r(pg, reg), mask)
#define PG_SET_MASK(reg, mask, value) \
do { \
uint32_t rv = pgraph_reg_r(pg, reg); \
SET_MASK(rv, mask, value); \
pgraph_reg_w(pg, reg, rv); \
} while (0)
NV2AState *g_nv2a;
uint64_t pgraph_read(void *opaque, hwaddr addr, unsigned int size)
@ -1532,6 +1523,14 @@ DEF_METHOD(NV097, SET_SHADE_MODE)
}
}
DEF_METHOD(NV097, SET_LINE_WIDTH)
{
if (parameter < 0x200) {
PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_LINEWIDTH,
parameter);
}
}
DEF_METHOD(NV097, SET_POLYGON_OFFSET_SCALE_FACTOR)
{
pgraph_reg_w(pg, NV_PGRAPH_ZOFFSETFACTOR, parameter);

View File

@ -297,6 +297,14 @@ static inline void pgraph_reg_w(PGRAPHState *pg, unsigned int r, uint32_t v)
pg->regs_[r] = v;
}
#define PG_GET_MASK(reg, mask) GET_MASK(pgraph_reg_r(pg, reg), mask)
#define PG_SET_MASK(reg, mask, value) \
do { \
uint32_t rv = pgraph_reg_r(pg, reg); \
SET_MASK(rv, mask, value); \
pgraph_reg_w(pg, reg, rv); \
} while (0)
void pgraph_clear_dirty_reg_map(PGRAPHState *pg);
static inline bool pgraph_is_reg_dirty(PGRAPHState *pg, unsigned int reg)
@ -369,6 +377,13 @@ static inline void pgraph_apply_scaling_factor(PGRAPHState *pg,
*height *= pg->surface_scale_factor;
}
static inline float pgraph_get_line_width(PGRAPHState *pg)
{
uint32_t line_width =
PG_GET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_LINEWIDTH);
return line_width / 8.0f; /* 6.3 format */
}
void pgraph_get_clear_color(PGRAPHState *pg, float rgba[4]);
void pgraph_get_clear_depth_stencil_value(PGRAPHState *pg, float *depth, int *stencil);

View File

@ -1521,7 +1521,8 @@ static void begin_draw(PGRAPHState *pg)
if (r->pipeline_binding->has_dynamic_line_width) {
float line_width =
clamp_line_width_to_device_limits(pg, pg->surface_scale_factor);
pgraph_get_line_width(pg) * pg->surface_scale_factor;
line_width = clamp_line_width_to_device_limits(pg, line_width);
vkCmdSetLineWidth(r->command_buffer, line_width);
}
}