/* * QEMU Geforce NV2A implementation * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel * Copyright (c) 2018-2024 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "../nv2a_int.h" #include "ui/xemu-notifications.h" #include "ui/xemu-settings.h" #include "util.h" #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) { NV2AState *d = (NV2AState *)opaque; PGRAPHState *pg = &d->pgraph; qemu_mutex_lock(&pg->lock); uint64_t r = 0; switch (addr) { case NV_PGRAPH_INTR: r = pg->pending_interrupts; break; case NV_PGRAPH_INTR_EN: r = pg->enabled_interrupts; break; case NV_PGRAPH_RDI_DATA: { unsigned int select = PG_GET_MASK(NV_PGRAPH_RDI_INDEX, NV_PGRAPH_RDI_INDEX_SELECT); unsigned int address = PG_GET_MASK(NV_PGRAPH_RDI_INDEX, NV_PGRAPH_RDI_INDEX_ADDRESS); r = pgraph_rdi_read(pg, select, address); /* FIXME: Overflow into select? */ assert(address < GET_MASK(NV_PGRAPH_RDI_INDEX_ADDRESS, NV_PGRAPH_RDI_INDEX_ADDRESS)); PG_SET_MASK(NV_PGRAPH_RDI_INDEX, NV_PGRAPH_RDI_INDEX_ADDRESS, address + 1); break; } default: r = pgraph_reg_r(pg, addr); break; } qemu_mutex_unlock(&pg->lock); nv2a_reg_log_read(NV_PGRAPH, addr, size, r); return r; } void pgraph_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { NV2AState *d = (NV2AState *)opaque; PGRAPHState *pg = &d->pgraph; nv2a_reg_log_write(NV_PGRAPH, addr, size, val); qemu_mutex_lock(&d->pfifo.lock); // FIXME: Factor out fifo lock here qemu_mutex_lock(&pg->lock); switch (addr) { case NV_PGRAPH_INTR: pg->pending_interrupts &= ~val; if (!(pg->pending_interrupts & NV_PGRAPH_INTR_ERROR)) { pg->waiting_for_nop = false; } if (!(pg->pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH)) { pg->waiting_for_context_switch = false; } pfifo_kick(d); break; case NV_PGRAPH_INTR_EN: pg->enabled_interrupts = val; break; case NV_PGRAPH_INCREMENT: if (val & NV_PGRAPH_INCREMENT_READ_3D) { PG_SET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_READ_3D, (PG_GET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_READ_3D)+1) % PG_GET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_MODULO_3D) ); nv2a_profile_increment(); pfifo_kick(d); } break; case NV_PGRAPH_RDI_DATA: { unsigned int select = PG_GET_MASK(NV_PGRAPH_RDI_INDEX, NV_PGRAPH_RDI_INDEX_SELECT); unsigned int address = PG_GET_MASK(NV_PGRAPH_RDI_INDEX, NV_PGRAPH_RDI_INDEX_ADDRESS); pgraph_rdi_write(pg, select, address, val); /* FIXME: Overflow into select? */ assert(address < GET_MASK(NV_PGRAPH_RDI_INDEX_ADDRESS, NV_PGRAPH_RDI_INDEX_ADDRESS)); PG_SET_MASK(NV_PGRAPH_RDI_INDEX, NV_PGRAPH_RDI_INDEX_ADDRESS, address + 1); break; } case NV_PGRAPH_CHANNEL_CTX_TRIGGER: { hwaddr context_address = PG_GET_MASK(NV_PGRAPH_CHANNEL_CTX_POINTER, NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4; if (val & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) { #ifdef DEBUG_NV2A unsigned pgraph_channel_id = PG_GET_MASK(NV_PGRAPH_CTX_USER, NV_PGRAPH_CTX_USER_CHID); #endif NV2A_DPRINTF("PGRAPH: read channel %d context from %" HWADDR_PRIx "\n", pgraph_channel_id, context_address); assert(context_address < memory_region_size(&d->ramin)); uint8_t *context_ptr = d->ramin_ptr + context_address; uint32_t context_user = ldl_le_p((uint32_t*)context_ptr); NV2A_DPRINTF(" - CTX_USER = 0x%x\n", context_user); pgraph_reg_w(pg, NV_PGRAPH_CTX_USER, context_user); // pgraph_set_context_user(d, context_user); } if (val & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) { /* do stuff ... */ } break; } default: pgraph_reg_w(pg, addr, val); break; } // events switch (addr) { case NV_PGRAPH_FIFO: pfifo_kick(d); break; } qemu_mutex_unlock(&pg->lock); qemu_mutex_unlock(&d->pfifo.lock); } void pgraph_context_switch(NV2AState *d, unsigned int channel_id) { PGRAPHState *pg = &d->pgraph; bool channel_valid = pgraph_reg_r(pg, NV_PGRAPH_CTX_CONTROL) & NV_PGRAPH_CTX_CONTROL_CHID; unsigned pgraph_channel_id = PG_GET_MASK(NV_PGRAPH_CTX_USER, NV_PGRAPH_CTX_USER_CHID); bool valid = channel_valid && pgraph_channel_id == channel_id; if (!valid) { PG_SET_MASK(NV_PGRAPH_TRAPPED_ADDR, NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id); NV2A_DPRINTF("pgraph switching to ch %d\n", channel_id); /* TODO: hardware context switching */ assert(!PG_GET_MASK(NV_PGRAPH_DEBUG_3, NV_PGRAPH_DEBUG_3_HW_CONTEXT_SWITCH)); pg->waiting_for_context_switch = true; qemu_mutex_unlock(&pg->lock); qemu_mutex_lock_iothread(); pg->pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; nv2a_update_irq(d); qemu_mutex_unlock_iothread(); qemu_mutex_lock(&pg->lock); } } static const PGRAPHRenderer *renderers[CONFIG_DISPLAY_RENDERER__COUNT]; void pgraph_renderer_register(const PGRAPHRenderer *renderer) { assert(renderer->type < CONFIG_DISPLAY_RENDERER__COUNT); renderers[renderer->type] = renderer; } void pgraph_init(NV2AState *d) { g_nv2a = d; PGRAPHState *pg = &d->pgraph; qemu_mutex_init(&pg->lock); qemu_mutex_init(&pg->renderer_lock); qemu_event_init(&pg->sync_complete, false); qemu_event_init(&pg->flush_complete, false); qemu_cond_init(&pg->framebuffer_released); pg->frame_time = 0; pg->draw_time = 0; pg->material_alpha = 0.0f; PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_SHADEMODE, NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH); pg->primitive_mode = PRIM_TYPE_INVALID; for (int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) { VertexAttribute *attribute = &pg->vertex_attributes[i]; attribute->inline_buffer = (float*)g_malloc(NV2A_MAX_BATCH_LENGTH * sizeof(float) * 4); attribute->inline_buffer_populated = false; } pgraph_clear_dirty_reg_map(pg); } void pgraph_clear_dirty_reg_map(PGRAPHState *pg) { memset(pg->regs_dirty, 0, sizeof(pg->regs_dirty)); } static CONFIG_DISPLAY_RENDERER get_default_renderer(void) { #ifdef CONFIG_OPENGL if (renderers[CONFIG_DISPLAY_RENDERER_OPENGL]) { return CONFIG_DISPLAY_RENDERER_OPENGL; } #endif #ifdef CONFIG_VULKAN if (renderers[CONFIG_DISPLAY_RENDERER_VULKAN]) { return CONFIG_DISPLAY_RENDERER_VULKAN; } #endif fprintf(stderr, "Warning: No available renderer\n"); return CONFIG_DISPLAY_RENDERER_NULL; } void nv2a_context_init(void) { if (!renderers[g_config.display.renderer]) { g_config.display.renderer = get_default_renderer(); fprintf(stderr, "Warning: Configured renderer unavailable. Switching to %s.\n", renderers[g_config.display.renderer]->name); } // FIXME: We need a mechanism for renderer to initialize new GL contexts // on the main thread at run time. For now, just let them all create // what they need. for (int i = 0; i < ARRAY_SIZE(renderers); i++) { const PGRAPHRenderer *r = renderers[i]; if (!r) { continue; } if (r->ops.early_context_init) { r->ops.early_context_init(); } } } static bool attempt_renderer_init(PGRAPHState *pg) { NV2AState *d = container_of(pg, NV2AState, pgraph); pg->renderer = renderers[g_config.display.renderer]; if (!pg->renderer) { xemu_queue_error_message("Configured renderer not available"); return false; } Error *local_err = NULL; if (pg->renderer->ops.init) { pg->renderer->ops.init(d, &local_err); } if (local_err) { const char *msg = error_get_pretty(local_err); xemu_queue_error_message(msg); error_free(local_err); local_err = NULL; return false; } return true; } static void init_renderer(PGRAPHState *pg) { if (attempt_renderer_init(pg)) { return; // Success } CONFIG_DISPLAY_RENDERER default_renderer = get_default_renderer(); if (default_renderer != g_config.display.renderer) { g_config.display.renderer = default_renderer; if (attempt_renderer_init(pg)) { g_autofree gchar *msg = g_strdup_printf( "Switched to default renderer: %s", pg->renderer->name); xemu_queue_notification(msg); return; } } // FIXME: Try others fprintf(stderr, "Fatal error: cannot initialize renderer\n"); exit(1); } void pgraph_init_thread(NV2AState *d) { init_renderer(&d->pgraph); } void pgraph_destroy(PGRAPHState *pg) { NV2AState *d = container_of(pg, NV2AState, pgraph); if (pg->renderer->ops.finalize) { pg->renderer->ops.finalize(d); } qemu_mutex_destroy(&pg->lock); } int nv2a_get_framebuffer_surface(void) { NV2AState *d = g_nv2a; PGRAPHState *pg = &d->pgraph; int s = 0; qemu_mutex_lock(&pg->renderer_lock); assert(!pg->framebuffer_in_use); pg->framebuffer_in_use = true; if (pg->renderer->ops.get_framebuffer_surface) { s = pg->renderer->ops.get_framebuffer_surface(d); } qemu_mutex_unlock(&pg->renderer_lock); return s; } void nv2a_release_framebuffer_surface(void) { NV2AState *d = g_nv2a; PGRAPHState *pg = &d->pgraph; qemu_mutex_lock(&pg->renderer_lock); pg->framebuffer_in_use = false; qemu_cond_broadcast(&pg->framebuffer_released); qemu_mutex_unlock(&pg->renderer_lock); } void nv2a_set_surface_scale_factor(unsigned int scale) { NV2AState *d = g_nv2a; qemu_mutex_unlock_iothread(); qemu_mutex_lock(&d->pgraph.renderer_lock); if (d->pgraph.renderer->ops.set_surface_scale_factor) { d->pgraph.renderer->ops.set_surface_scale_factor(d, scale); } qemu_mutex_unlock(&d->pgraph.renderer_lock); qemu_mutex_lock_iothread(); } unsigned int nv2a_get_surface_scale_factor(void) { NV2AState *d = g_nv2a; int s = 1; qemu_mutex_unlock_iothread(); qemu_mutex_lock(&d->pgraph.renderer_lock); if (d->pgraph.renderer->ops.get_surface_scale_factor) { s = d->pgraph.renderer->ops.get_surface_scale_factor(d); } qemu_mutex_unlock(&d->pgraph.renderer_lock); qemu_mutex_lock_iothread(); return s; } #define METHOD_ADDR(gclass, name) \ gclass ## _ ## name #define METHOD_ADDR_TO_INDEX(x) ((x)>>2) #define METHOD_NAME_STR(gclass, name) \ tostring(gclass ## _ ## name) #define METHOD_FUNC_NAME(gclass, name) \ pgraph_ ## gclass ## _ ## name ## _handler #define METHOD_HANDLER_ARG_DECL \ NV2AState *d, PGRAPHState *pg, \ unsigned int subchannel, unsigned int method, \ uint32_t parameter, uint32_t *parameters, \ size_t num_words_available, size_t *num_words_consumed, bool inc #define METHOD_HANDLER_ARGS \ d, pg, subchannel, method, parameter, parameters, \ num_words_available, num_words_consumed, inc #define DEF_METHOD_PROTO(gclass, name) \ static void METHOD_FUNC_NAME(gclass, name)(METHOD_HANDLER_ARG_DECL) #define DEF_METHOD(gclass, name) \ DEF_METHOD_PROTO(gclass, name); #define DEF_METHOD_RANGE(gclass, name, range) \ DEF_METHOD_PROTO(gclass, name); #define DEF_METHOD_CASE_4_OFFSET(gclass, name, offset, stride) /* Drop */ #define DEF_METHOD_CASE_4(gclass, name, stride) \ DEF_METHOD_PROTO(gclass, name); #include "methods.h" #undef DEF_METHOD #undef DEF_METHOD_RANGE #undef DEF_METHOD_CASE_4_OFFSET #undef DEF_METHOD_CASE_4 typedef void (*MethodFunc)(METHOD_HANDLER_ARG_DECL); static const struct { uint32_t base; const char *name; MethodFunc handler; } pgraph_kelvin_methods[0x800] = { #define DEF_METHOD(gclass, name) \ [METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name))] = \ { \ METHOD_ADDR(gclass, name), \ METHOD_NAME_STR(gclass, name), \ METHOD_FUNC_NAME(gclass, name), \ }, #define DEF_METHOD_RANGE(gclass, name, range) \ [METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name)) \ ... METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name) + 4*range - 1)] = \ { \ METHOD_ADDR(gclass, name), \ METHOD_NAME_STR(gclass, name), \ METHOD_FUNC_NAME(gclass, name), \ }, #define DEF_METHOD_CASE_4_OFFSET(gclass, name, offset, stride) \ [METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name) + offset)] = \ { \ METHOD_ADDR(gclass, name), \ METHOD_NAME_STR(gclass, name), \ METHOD_FUNC_NAME(gclass, name), \ }, \ [METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name) + offset + stride)] = \ { \ METHOD_ADDR(gclass, name), \ METHOD_NAME_STR(gclass, name), \ METHOD_FUNC_NAME(gclass, name), \ }, \ [METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name) + offset + stride * 2)] = \ { \ METHOD_ADDR(gclass, name), \ METHOD_NAME_STR(gclass, name), \ METHOD_FUNC_NAME(gclass, name), \ }, \ [METHOD_ADDR_TO_INDEX(METHOD_ADDR(gclass, name) + offset + stride * 3)] = \ { \ METHOD_ADDR(gclass, name), \ METHOD_NAME_STR(gclass, name), \ METHOD_FUNC_NAME(gclass, name), \ }, #define DEF_METHOD_CASE_4(gclass, name, stride) \ DEF_METHOD_CASE_4_OFFSET(gclass, name, 0, stride) #include "methods.h" #undef DEF_METHOD #undef DEF_METHOD_RANGE #undef DEF_METHOD_CASE_4_OFFSET #undef DEF_METHOD_CASE_4 }; #define METHOD_RANGE_END_NAME(gclass, name) \ pgraph_ ## gclass ## _ ## name ## __END #define DEF_METHOD(gclass, name) \ static const size_t METHOD_RANGE_END_NAME(gclass, name) = \ METHOD_ADDR(gclass, name) + 4; #define DEF_METHOD_RANGE(gclass, name, range) \ static const size_t METHOD_RANGE_END_NAME(gclass, name) = \ METHOD_ADDR(gclass, name) + 4*range; #define DEF_METHOD_CASE_4_OFFSET(gclass, name, offset, stride) /* drop */ #define DEF_METHOD_CASE_4(gclass, name, stride) \ static const size_t METHOD_RANGE_END_NAME(gclass, name) = \ METHOD_ADDR(gclass, name) + 4*stride; #include "methods.h" #undef DEF_METHOD #undef DEF_METHOD_RANGE #undef DEF_METHOD_CASE_4_OFFSET #undef DEF_METHOD_CASE_4 static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter) { const char *method_name = "?"; static unsigned int last = 0; static unsigned int count = 0; if (last == NV097_ARRAY_ELEMENT16 && method != last) { method_name = "NV097_ARRAY_ELEMENT16"; trace_nv2a_pgraph_method_abbrev(subchannel, graphics_class, last, method_name, count); } if (method != NV097_ARRAY_ELEMENT16) { uint32_t base = method; switch (graphics_class) { case NV_KELVIN_PRIMITIVE: { int idx = METHOD_ADDR_TO_INDEX(method); if (idx < ARRAY_SIZE(pgraph_kelvin_methods) && pgraph_kelvin_methods[idx].handler) { method_name = pgraph_kelvin_methods[idx].name; base = pgraph_kelvin_methods[idx].base; } break; } default: break; } uint32_t offset = method - base; trace_nv2a_pgraph_method(subchannel, graphics_class, method, method_name, offset, parameter); } if (method == last) { count++; } else { count = 0; } last = method; } static void pgraph_method_inc(MethodFunc handler, uint32_t end, METHOD_HANDLER_ARG_DECL) { if (!inc) { handler(METHOD_HANDLER_ARGS); return; } size_t count = MIN(num_words_available, (end - method) / 4); for (size_t i = 0; i < count; i++) { parameter = ldl_le_p(parameters + i); if (i) { pgraph_method_log(subchannel, NV_KELVIN_PRIMITIVE, method, parameter); } handler(METHOD_HANDLER_ARGS); method += 4; } *num_words_consumed = count; } static void pgraph_method_non_inc(MethodFunc handler, METHOD_HANDLER_ARG_DECL) { if (inc) { handler(METHOD_HANDLER_ARGS); return; } for (size_t i = 0; i < num_words_available; i++) { parameter = ldl_le_p(parameters + i); if (i) { pgraph_method_log(subchannel, NV_KELVIN_PRIMITIVE, method, parameter); } handler(METHOD_HANDLER_ARGS); } *num_words_consumed = num_words_available; } #define METHOD_FUNC_NAME_INT(gclass, name) METHOD_FUNC_NAME(gclass, name##_int) #define DEF_METHOD_INT(gclass, name) DEF_METHOD(gclass, name##_int) #define DEF_METHOD(gclass, name) DEF_METHOD_PROTO(gclass, name) #define DEF_METHOD_INC(gclass, name) \ DEF_METHOD_INT(gclass, name); \ DEF_METHOD(gclass, name) \ { \ pgraph_method_inc(METHOD_FUNC_NAME_INT(gclass, name), \ METHOD_RANGE_END_NAME(gclass, name), \ METHOD_HANDLER_ARGS); \ } \ DEF_METHOD_INT(gclass, name) #define DEF_METHOD_NON_INC(gclass, name) \ DEF_METHOD_INT(gclass, name); \ DEF_METHOD(gclass, name) \ { \ pgraph_method_non_inc(METHOD_FUNC_NAME_INT(gclass, name), \ METHOD_HANDLER_ARGS); \ } \ DEF_METHOD_INT(gclass, name) int pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter, uint32_t *parameters, size_t num_words_available, size_t max_lookahead_words, bool inc) { int num_processed = 1; PGRAPHState *pg = &d->pgraph; bool channel_valid = PG_GET_MASK(NV_PGRAPH_CTX_CONTROL, NV_PGRAPH_CTX_CONTROL_CHID); assert(channel_valid); ContextSurfaces2DState *context_surfaces_2d = &pg->context_surfaces_2d; ImageBlitState *image_blit = &pg->image_blit; BetaState *beta = &pg->beta; assert(subchannel < 8); if (method == NV_SET_OBJECT) { assert(parameter < memory_region_size(&d->ramin)); uint8_t *obj_ptr = d->ramin_ptr + parameter; uint32_t ctx_1 = ldl_le_p((uint32_t*)obj_ptr); uint32_t ctx_2 = ldl_le_p((uint32_t*)(obj_ptr+4)); uint32_t ctx_3 = ldl_le_p((uint32_t*)(obj_ptr+8)); uint32_t ctx_4 = ldl_le_p((uint32_t*)(obj_ptr+12)); uint32_t ctx_5 = parameter; pgraph_reg_w(pg, NV_PGRAPH_CTX_CACHE1 + subchannel * 4, ctx_1); pgraph_reg_w(pg, NV_PGRAPH_CTX_CACHE2 + subchannel * 4, ctx_2); pgraph_reg_w(pg, NV_PGRAPH_CTX_CACHE3 + subchannel * 4, ctx_3); pgraph_reg_w(pg, NV_PGRAPH_CTX_CACHE4 + subchannel * 4, ctx_4); pgraph_reg_w(pg, NV_PGRAPH_CTX_CACHE5 + subchannel * 4, ctx_5); } // is this right? pgraph_reg_w(pg, NV_PGRAPH_CTX_SWITCH1, pgraph_reg_r(pg, NV_PGRAPH_CTX_CACHE1 + subchannel * 4)); pgraph_reg_w(pg, NV_PGRAPH_CTX_SWITCH2, pgraph_reg_r(pg, NV_PGRAPH_CTX_CACHE2 + subchannel * 4)); pgraph_reg_w(pg, NV_PGRAPH_CTX_SWITCH3, pgraph_reg_r(pg, NV_PGRAPH_CTX_CACHE3 + subchannel * 4)); pgraph_reg_w(pg, NV_PGRAPH_CTX_SWITCH4, pgraph_reg_r(pg, NV_PGRAPH_CTX_CACHE4 + subchannel * 4)); pgraph_reg_w(pg, NV_PGRAPH_CTX_SWITCH5, pgraph_reg_r(pg, NV_PGRAPH_CTX_CACHE5 + subchannel * 4)); uint32_t graphics_class = PG_GET_MASK(NV_PGRAPH_CTX_SWITCH1, NV_PGRAPH_CTX_SWITCH1_GRCLASS); pgraph_method_log(subchannel, graphics_class, method, parameter); if (subchannel != 0) { // catches context switching issues on xbox d3d assert(graphics_class != 0x97); } /* ugly switch for now */ switch (graphics_class) { case NV_BETA: { switch (method) { case NV012_SET_OBJECT: beta->object_instance = parameter; break; case NV012_SET_BETA: if (parameter & 0x80000000) { beta->beta = 0; } else { // The parameter is a signed fixed-point number with a sign bit // and 31 fractional bits. Note that negative values are clamped // to 0, and only 8 fractional bits are actually implemented in // hardware. beta->beta = parameter & 0x7f800000; } break; default: goto unhandled; } break; } case NV_CONTEXT_PATTERN: { switch (method) { case NV044_SET_MONOCHROME_COLOR0: pgraph_reg_w(pg, NV_PGRAPH_PATT_COLOR0, parameter); break; default: goto unhandled; } break; } case NV_CONTEXT_SURFACES_2D: { switch (method) { case NV062_SET_OBJECT: context_surfaces_2d->object_instance = parameter; break; case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE: context_surfaces_2d->dma_image_source = parameter; break; case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN: context_surfaces_2d->dma_image_dest = parameter; break; case NV062_SET_COLOR_FORMAT: context_surfaces_2d->color_format = parameter; break; case NV062_SET_PITCH: context_surfaces_2d->source_pitch = parameter & 0xFFFF; context_surfaces_2d->dest_pitch = parameter >> 16; break; case NV062_SET_OFFSET_SOURCE: context_surfaces_2d->source_offset = parameter & 0x07FFFFFF; break; case NV062_SET_OFFSET_DESTIN: context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF; break; default: goto unhandled; } break; } case NV_IMAGE_BLIT: { switch (method) { case NV09F_SET_OBJECT: image_blit->object_instance = parameter; break; case NV09F_SET_CONTEXT_SURFACES: image_blit->context_surfaces = parameter; break; case NV09F_SET_OPERATION: image_blit->operation = parameter; break; case NV09F_CONTROL_POINT_IN: image_blit->in_x = parameter & 0xFFFF; image_blit->in_y = parameter >> 16; break; case NV09F_CONTROL_POINT_OUT: image_blit->out_x = parameter & 0xFFFF; image_blit->out_y = parameter >> 16; break; case NV09F_SIZE: image_blit->width = parameter & 0xFFFF; image_blit->height = parameter >> 16; if (image_blit->width && image_blit->height) { d->pgraph.renderer->ops.image_blit(d); } break; default: goto unhandled; } break; } case NV_KELVIN_PRIMITIVE: { MethodFunc handler = pgraph_kelvin_methods[METHOD_ADDR_TO_INDEX(method)].handler; if (handler == NULL) { goto unhandled; } size_t num_words_consumed = 1; handler(d, pg, subchannel, method, parameter, parameters, num_words_available, &num_words_consumed, inc); /* Squash repeated BEGIN,DRAW_ARRAYS,END */ #define LAM(i, mthd) ((parameters[i*2+1] & 0x31fff) == (mthd)) #define LAP(i, prm) (parameters[i*2+2] == (prm)) #define LAMP(i, mthd, prm) (LAM(i, mthd) && LAP(i, prm)) if (method == NV097_DRAW_ARRAYS && (max_lookahead_words >= 7) && pg->inline_elements_length == 0 && pg->draw_arrays_length < (ARRAY_SIZE(pg->draw_arrays_start) - 1) && LAMP(0, NV097_SET_BEGIN_END, NV097_SET_BEGIN_END_OP_END) && LAMP(1, NV097_SET_BEGIN_END, pg->primitive_mode) && LAM(2, NV097_DRAW_ARRAYS)) { num_words_consumed += 4; pg->draw_arrays_prevent_connect = true; } #undef LAM #undef LAP #undef LAMP num_processed = num_words_consumed; break; } default: goto unhandled; } return num_processed; unhandled: trace_nv2a_pgraph_method_unhandled(subchannel, graphics_class, method, parameter); return num_processed; } DEF_METHOD(NV097, SET_OBJECT) { pg->kelvin.object_instance = parameter; } DEF_METHOD(NV097, NO_OPERATION) { /* The bios uses nop as a software method call - * it seems to expect a notify interrupt if the parameter isn't 0. * According to a nouveau guy it should still be a nop regardless * of the parameter. It's possible a debug register enables this, * but nothing obvious sticks out. Weird. */ if (parameter == 0) { return; } unsigned channel_id = PG_GET_MASK(NV_PGRAPH_CTX_USER, NV_PGRAPH_CTX_USER_CHID); assert(!(pg->pending_interrupts & NV_PGRAPH_INTR_ERROR)); PG_SET_MASK(NV_PGRAPH_TRAPPED_ADDR, NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id); PG_SET_MASK(NV_PGRAPH_TRAPPED_ADDR, NV_PGRAPH_TRAPPED_ADDR_SUBCH, subchannel); PG_SET_MASK(NV_PGRAPH_TRAPPED_ADDR, NV_PGRAPH_TRAPPED_ADDR_MTHD, method); pgraph_reg_w(pg, NV_PGRAPH_TRAPPED_DATA_LOW, parameter); pgraph_reg_w(pg, NV_PGRAPH_NSOURCE, NV_PGRAPH_NSOURCE_NOTIFICATION); /* TODO: check this */ pg->pending_interrupts |= NV_PGRAPH_INTR_ERROR; pg->waiting_for_nop = true; qemu_mutex_unlock(&pg->lock); qemu_mutex_lock_iothread(); nv2a_update_irq(d); qemu_mutex_unlock_iothread(); qemu_mutex_lock(&pg->lock); } DEF_METHOD(NV097, WAIT_FOR_IDLE) { d->pgraph.renderer->ops.surface_update(d, false, true, true); } DEF_METHOD(NV097, SET_FLIP_READ) { PG_SET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_READ_3D, parameter); } DEF_METHOD(NV097, SET_FLIP_WRITE) { PG_SET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_WRITE_3D, parameter); } DEF_METHOD(NV097, SET_FLIP_MODULO) { PG_SET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_MODULO_3D, parameter); } DEF_METHOD(NV097, FLIP_INCREMENT_WRITE) { uint32_t old = PG_GET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_WRITE_3D); PG_SET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_WRITE_3D, (PG_GET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_WRITE_3D)+1) % PG_GET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_MODULO_3D) ); uint32_t new = PG_GET_MASK(NV_PGRAPH_SURFACE, NV_PGRAPH_SURFACE_WRITE_3D); trace_nv2a_pgraph_flip_increment_write(old, new); pg->frame_time++; } DEF_METHOD(NV097, FLIP_STALL) { trace_nv2a_pgraph_flip_stall(); d->pgraph.renderer->ops.surface_update(d, false, true, true); d->pgraph.renderer->ops.flip_stall(d); nv2a_profile_flip_stall(); pg->waiting_for_flip = true; } // TODO: these should be loading the dma objects from ramin here? DEF_METHOD(NV097, SET_CONTEXT_DMA_NOTIFIES) { pg->dma_notifies = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_A) { pg->dma_a = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_B) { pg->dma_b = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_STATE) { pg->dma_state = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_COLOR) { /* try to get any straggling draws in before the surface's changed :/ */ d->pgraph.renderer->ops.surface_update(d, false, true, true); pg->dma_color = parameter; pg->surface_color.buffer_dirty = true; } DEF_METHOD(NV097, SET_CONTEXT_DMA_ZETA) { pg->dma_zeta = parameter; pg->surface_zeta.buffer_dirty = true; } DEF_METHOD(NV097, SET_CONTEXT_DMA_VERTEX_A) { pg->dma_vertex_a = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_VERTEX_B) { pg->dma_vertex_b = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_SEMAPHORE) { pg->dma_semaphore = parameter; } DEF_METHOD(NV097, SET_CONTEXT_DMA_REPORT) { d->pgraph.renderer->ops.process_pending_reports(d); pg->dma_report = parameter; } DEF_METHOD(NV097, SET_SURFACE_CLIP_HORIZONTAL) { d->pgraph.renderer->ops.surface_update(d, false, true, true); pg->surface_shape.clip_x = GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X); pg->surface_shape.clip_width = GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH); } DEF_METHOD(NV097, SET_SURFACE_CLIP_VERTICAL) { d->pgraph.renderer->ops.surface_update(d, false, true, true); pg->surface_shape.clip_y = GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y); pg->surface_shape.clip_height = GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT); } DEF_METHOD(NV097, SET_SURFACE_FORMAT) { d->pgraph.renderer->ops.surface_update(d, false, true, true); pg->surface_shape.color_format = GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR); pg->surface_shape.zeta_format = GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA); pg->surface_shape.anti_aliasing = GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING); pg->surface_shape.log_width = GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH); pg->surface_shape.log_height = GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT); int surface_type = GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE); if (surface_type != pg->surface_type) { pg->surface_type = surface_type; pg->surface_color.buffer_dirty = true; pg->surface_zeta.buffer_dirty = true; } } DEF_METHOD(NV097, SET_SURFACE_PITCH) { d->pgraph.renderer->ops.surface_update(d, false, true, true); unsigned int color_pitch = GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR); unsigned int zeta_pitch = GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA); pg->surface_color.buffer_dirty |= (pg->surface_color.pitch != color_pitch); pg->surface_color.pitch = color_pitch; pg->surface_zeta.buffer_dirty |= (pg->surface_zeta.pitch != zeta_pitch); pg->surface_zeta.pitch = zeta_pitch; } DEF_METHOD(NV097, SET_SURFACE_COLOR_OFFSET) { d->pgraph.renderer->ops.surface_update(d, false, true, true); pg->surface_color.buffer_dirty |= (pg->surface_color.offset != parameter); pg->surface_color.offset = parameter; } DEF_METHOD(NV097, SET_SURFACE_ZETA_OFFSET) { d->pgraph.renderer->ops.surface_update(d, false, true, true); pg->surface_zeta.buffer_dirty |= (pg->surface_zeta.offset != parameter); pg->surface_zeta.offset = parameter; } DEF_METHOD_INC(NV097, SET_COMBINER_ALPHA_ICW) { int slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; pgraph_reg_w(pg, NV_PGRAPH_COMBINEALPHAI0 + slot * 4, parameter); } DEF_METHOD(NV097, SET_COMBINER_SPECULAR_FOG_CW0) { pgraph_reg_w(pg, NV_PGRAPH_COMBINESPECFOG0, parameter); } DEF_METHOD(NV097, SET_COMBINER_SPECULAR_FOG_CW1) { pgraph_reg_w(pg, NV_PGRAPH_COMBINESPECFOG1, parameter); } DEF_METHOD(NV097, SET_TEXTURE_ADDRESS) { int slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64; pgraph_reg_w(pg, NV_PGRAPH_TEXADDRESS0 + slot * 4, parameter); } DEF_METHOD(NV097, SET_CONTROL0) { d->pgraph.renderer->ops.surface_update(d, false, true, true); bool stencil_write_enable = parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE; PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE, stencil_write_enable); uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT); PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format); bool z_perspective = parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE; PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, z_perspective); } DEF_METHOD(NV097, SET_COLOR_MATERIAL) { PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_EMISSION, (parameter >> 0) & 3); PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_AMBIENT, (parameter >> 2) & 3); PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_DIFFUSE, (parameter >> 4) & 3); PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_SPECULAR, (parameter >> 6) & 3); } DEF_METHOD(NV097, SET_FOG_MODE) { /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */ unsigned int mode; switch (parameter) { case NV097_SET_FOG_MODE_V_LINEAR: mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break; case NV097_SET_FOG_MODE_V_EXP: mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break; case NV097_SET_FOG_MODE_V_EXP2: mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break; case NV097_SET_FOG_MODE_V_EXP_ABS: mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break; case NV097_SET_FOG_MODE_V_EXP2_ABS: mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break; case NV097_SET_FOG_MODE_V_LINEAR_ABS: mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break; default: assert(false); break; } PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_FOG_MODE, mode); } DEF_METHOD(NV097, SET_FOG_GEN_MODE) { unsigned int mode; switch (parameter) { case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA: mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break; case NV097_SET_FOG_GEN_MODE_V_RADIAL: mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break; case NV097_SET_FOG_GEN_MODE_V_PLANAR: mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break; case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR: mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break; case NV097_SET_FOG_GEN_MODE_V_FOG_X: mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break; default: assert(false); break; } PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_FOGGENMODE, mode); } DEF_METHOD(NV097, SET_FOG_ENABLE) { /* FIXME: There is also: PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_FOGENABLE, parameter); */ PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_FOGENABLE, parameter); } DEF_METHOD(NV097, SET_FOG_COLOR) { /* PGRAPH channels are ARGB, parameter channels are ABGR */ uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED); uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN); uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE); uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA); PG_SET_MASK(NV_PGRAPH_FOGCOLOR, NV_PGRAPH_FOGCOLOR_RED, red); PG_SET_MASK(NV_PGRAPH_FOGCOLOR, NV_PGRAPH_FOGCOLOR_GREEN, green); PG_SET_MASK(NV_PGRAPH_FOGCOLOR, NV_PGRAPH_FOGCOLOR_BLUE, blue); PG_SET_MASK(NV_PGRAPH_FOGCOLOR, NV_PGRAPH_FOGCOLOR_ALPHA, alpha); } DEF_METHOD(NV097, SET_WINDOW_CLIP_TYPE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE, parameter); } DEF_METHOD_INC(NV097, SET_WINDOW_CLIP_HORIZONTAL) { int slot = (method - NV097_SET_WINDOW_CLIP_HORIZONTAL) / 4; for (; slot < 8; ++slot) { pgraph_reg_w(pg, NV_PGRAPH_WINDOWCLIPX0 + slot * 4, parameter); } } DEF_METHOD_INC(NV097, SET_WINDOW_CLIP_VERTICAL) { int slot = (method - NV097_SET_WINDOW_CLIP_VERTICAL) / 4; for (; slot < 8; ++slot) { pgraph_reg_w(pg, NV_PGRAPH_WINDOWCLIPY0 + slot * 4, parameter); } } DEF_METHOD(NV097, SET_ALPHA_TEST_ENABLE) { PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter); } DEF_METHOD(NV097, SET_BLEND_ENABLE) { PG_SET_MASK(NV_PGRAPH_BLEND, NV_PGRAPH_BLEND_EN, parameter); } DEF_METHOD(NV097, SET_CULL_FACE_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_CULLENABLE, parameter); } DEF_METHOD(NV097, SET_DEPTH_TEST_ENABLE) { PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ZENABLE, parameter); } DEF_METHOD(NV097, SET_DITHER_ENABLE) { PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter); } DEF_METHOD(NV097, SET_LIGHTING_ENABLE) { PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_LIGHTING, parameter); } DEF_METHOD(NV097, SET_POINT_PARAMS_ENABLE) { PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_POINTPARAMSENABLE, parameter); PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_POINTPARAMSENABLE, parameter); } DEF_METHOD(NV097, SET_POINT_SMOOTH_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_POINTSMOOTHENABLE, parameter); } DEF_METHOD(NV097, SET_LINE_SMOOTH_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_LINESMOOTHENABLE, parameter); } DEF_METHOD(NV097, SET_POLY_SMOOTH_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_POLYSMOOTHENABLE, parameter); } DEF_METHOD(NV097, SET_SKIN_MODE) { PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_SKIN, parameter); } DEF_METHOD(NV097, SET_STENCIL_TEST_ENABLE) { PG_SET_MASK(NV_PGRAPH_CONTROL_1, NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter); } DEF_METHOD(NV097, SET_POLY_OFFSET_POINT_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter); } DEF_METHOD(NV097, SET_POLY_OFFSET_LINE_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter); } DEF_METHOD(NV097, SET_POLY_OFFSET_FILL_ENABLE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter); } DEF_METHOD(NV097, SET_ALPHA_FUNC) { PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF); } DEF_METHOD(NV097, SET_ALPHA_REF) { PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ALPHAREF, parameter); } DEF_METHOD(NV097, SET_BLEND_FUNC_SFACTOR) { unsigned int factor; switch (parameter) { case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO: factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE: factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR: factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR: factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA: factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA: factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA: factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA: factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR: factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR: factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE: factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR: factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR: factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA: factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break; case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; default: NV2A_DPRINTF("Unknown blend source factor: 0x%08x\n", parameter); return; /* discard */ } PG_SET_MASK(NV_PGRAPH_BLEND, NV_PGRAPH_BLEND_SFACTOR, factor); } DEF_METHOD(NV097, SET_BLEND_FUNC_DFACTOR) { unsigned int factor; switch (parameter) { case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO: factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE: factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR: factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR: factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA: factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA: factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA: factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA: factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR: factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR: factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE: factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR: factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR: factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA: factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break; case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; default: NV2A_DPRINTF("Unknown blend destination factor: 0x%08x\n", parameter); return; /* discard */ } PG_SET_MASK(NV_PGRAPH_BLEND, NV_PGRAPH_BLEND_DFACTOR, factor); } DEF_METHOD(NV097, SET_BLEND_COLOR) { pgraph_reg_w(pg, NV_PGRAPH_BLENDCOLOR, parameter); } DEF_METHOD(NV097, SET_BLEND_EQUATION) { unsigned int equation; switch (parameter) { case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT: equation = 0; break; case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT: equation = 1; break; case NV097_SET_BLEND_EQUATION_V_FUNC_ADD: equation = 2; break; case NV097_SET_BLEND_EQUATION_V_MIN: equation = 3; break; case NV097_SET_BLEND_EQUATION_V_MAX: equation = 4; break; case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED: equation = 5; break; case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED: equation = 6; break; default: NV2A_DPRINTF("Unknown blend equation: 0x%08x\n", parameter); return; /* discard */ } PG_SET_MASK(NV_PGRAPH_BLEND, NV_PGRAPH_BLEND_EQN, equation); } DEF_METHOD(NV097, SET_DEPTH_FUNC) { PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ZFUNC, parameter & 0xF); } DEF_METHOD(NV097, SET_COLOR_MASK) { pg->surface_color.write_enabled_cache |= pgraph_color_write_enabled(pg); bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE; bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE; bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE; bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE; PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha); PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red); PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green); PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue); } DEF_METHOD(NV097, SET_DEPTH_MASK) { pg->surface_zeta.write_enabled_cache |= pgraph_zeta_write_enabled(pg); PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter); } DEF_METHOD(NV097, SET_STENCIL_MASK) { PG_SET_MASK(NV_PGRAPH_CONTROL_1, NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter); } DEF_METHOD(NV097, SET_STENCIL_FUNC) { PG_SET_MASK(NV_PGRAPH_CONTROL_1, NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF); } DEF_METHOD(NV097, SET_STENCIL_FUNC_REF) { PG_SET_MASK(NV_PGRAPH_CONTROL_1, NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter); } DEF_METHOD(NV097, SET_STENCIL_FUNC_MASK) { PG_SET_MASK(NV_PGRAPH_CONTROL_1, NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter); } static unsigned int kelvin_map_stencil_op(uint32_t parameter) { unsigned int op; switch (parameter) { case NV097_SET_STENCIL_OP_V_KEEP: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_KEEP; break; case NV097_SET_STENCIL_OP_V_ZERO: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_ZERO; break; case NV097_SET_STENCIL_OP_V_REPLACE: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_REPLACE; break; case NV097_SET_STENCIL_OP_V_INCRSAT: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCRSAT; break; case NV097_SET_STENCIL_OP_V_DECRSAT: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECRSAT; break; case NV097_SET_STENCIL_OP_V_INVERT: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INVERT; break; case NV097_SET_STENCIL_OP_V_INCR: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCR; break; case NV097_SET_STENCIL_OP_V_DECR: op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECR; break; default: assert(false); break; } return op; } DEF_METHOD(NV097, SET_STENCIL_OP_FAIL) { PG_SET_MASK(NV_PGRAPH_CONTROL_2, NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL, kelvin_map_stencil_op(parameter)); } DEF_METHOD(NV097, SET_STENCIL_OP_ZFAIL) { PG_SET_MASK(NV_PGRAPH_CONTROL_2, NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL, kelvin_map_stencil_op(parameter)); } DEF_METHOD(NV097, SET_STENCIL_OP_ZPASS) { PG_SET_MASK(NV_PGRAPH_CONTROL_2, NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS, kelvin_map_stencil_op(parameter)); } DEF_METHOD(NV097, SET_SHADE_MODE) { switch (parameter) { case NV097_SET_SHADE_MODE_V_FLAT: PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_SHADEMODE, NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT); break; case NV097_SET_SHADE_MODE_V_SMOOTH: PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_SHADEMODE, NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH); break; default: /* Discard */ break; } } DEF_METHOD(NV097, SET_POLYGON_OFFSET_SCALE_FACTOR) { pgraph_reg_w(pg, NV_PGRAPH_ZOFFSETFACTOR, parameter); } DEF_METHOD(NV097, SET_POLYGON_OFFSET_BIAS) { pgraph_reg_w(pg, NV_PGRAPH_ZOFFSETBIAS, parameter); } static unsigned int kelvin_map_polygon_mode(uint32_t parameter) { unsigned int mode; switch (parameter) { case NV097_SET_FRONT_POLYGON_MODE_V_POINT: mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_POINT; break; case NV097_SET_FRONT_POLYGON_MODE_V_LINE: mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_LINE; break; case NV097_SET_FRONT_POLYGON_MODE_V_FILL: mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_FILL; break; default: assert(false); break; } return mode; } DEF_METHOD(NV097, SET_FRONT_POLYGON_MODE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_FRONTFACEMODE, kelvin_map_polygon_mode(parameter)); } DEF_METHOD(NV097, SET_BACK_POLYGON_MODE) { PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_BACKFACEMODE, kelvin_map_polygon_mode(parameter)); } DEF_METHOD(NV097, SET_CLIP_MIN) { pgraph_reg_w(pg, NV_PGRAPH_ZCLIPMIN, parameter); } DEF_METHOD(NV097, SET_CLIP_MAX) { pgraph_reg_w(pg, NV_PGRAPH_ZCLIPMAX, parameter); } DEF_METHOD(NV097, SET_CULL_FACE) { unsigned int face; switch (parameter) { case NV097_SET_CULL_FACE_V_FRONT: face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break; case NV097_SET_CULL_FACE_V_BACK: face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break; case NV097_SET_CULL_FACE_V_FRONT_AND_BACK: face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break; default: assert(false); break; } PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_CULLCTRL, face); } DEF_METHOD(NV097, SET_FRONT_FACE) { bool ccw; switch (parameter) { case NV097_SET_FRONT_FACE_V_CW: ccw = false; break; case NV097_SET_FRONT_FACE_V_CCW: ccw = true; break; default: NV2A_DPRINTF("Unknown front face: 0x%08x\n", parameter); return; /* discard */ } PG_SET_MASK(NV_PGRAPH_SETUPRASTER, NV_PGRAPH_SETUPRASTER_FRONTFACE, ccw ? 1 : 0); } DEF_METHOD(NV097, SET_NORMALIZATION_ENABLE) { PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, parameter); } DEF_METHOD_INC(NV097, SET_MATERIAL_EMISSION) { int slot = (method - NV097_SET_MATERIAL_EMISSION) / 4; // FIXME: Verify NV_IGRAPH_XF_LTCTXA_CM_COL is correct pg->ltctxa[NV_IGRAPH_XF_LTCTXA_CM_COL][slot] = parameter; pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_CM_COL] = true; } DEF_METHOD(NV097, SET_MATERIAL_ALPHA) { pg->material_alpha = *(float*)¶meter; } DEF_METHOD(NV097, SET_LIGHT_ENABLE_MASK) { PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_LIGHTS, parameter); } static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel) { assert(channel < 4); unsigned int texgen; switch (parameter) { case NV097_SET_TEXGEN_S_DISABLE: texgen = NV_PGRAPH_CSV1_A_T0_S_DISABLE; break; case NV097_SET_TEXGEN_S_EYE_LINEAR: texgen = NV_PGRAPH_CSV1_A_T0_S_EYE_LINEAR; break; case NV097_SET_TEXGEN_S_OBJECT_LINEAR: texgen = NV_PGRAPH_CSV1_A_T0_S_OBJECT_LINEAR; break; case NV097_SET_TEXGEN_S_SPHERE_MAP: assert(channel < 2); texgen = NV_PGRAPH_CSV1_A_T0_S_SPHERE_MAP; break; case NV097_SET_TEXGEN_S_REFLECTION_MAP: assert(channel < 3); texgen = NV_PGRAPH_CSV1_A_T0_S_REFLECTION_MAP; break; case NV097_SET_TEXGEN_S_NORMAL_MAP: assert(channel < 3); texgen = NV_PGRAPH_CSV1_A_T0_S_NORMAL_MAP; break; default: assert(false); break; } return texgen; } DEF_METHOD(NV097, SET_TEXGEN_S) { int slot = (method - NV097_SET_TEXGEN_S) / 16; unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S : NV_PGRAPH_CSV1_A_T0_S; PG_SET_MASK(reg, mask, kelvin_map_texgen(parameter, 0)); } DEF_METHOD(NV097, SET_TEXGEN_T) { int slot = (method - NV097_SET_TEXGEN_T) / 16; unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T : NV_PGRAPH_CSV1_A_T0_T; PG_SET_MASK(reg, mask, kelvin_map_texgen(parameter, 1)); } DEF_METHOD(NV097, SET_TEXGEN_R) { int slot = (method - NV097_SET_TEXGEN_R) / 16; unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R : NV_PGRAPH_CSV1_A_T0_R; PG_SET_MASK(reg, mask, kelvin_map_texgen(parameter, 2)); } DEF_METHOD(NV097, SET_TEXGEN_Q) { int slot = (method - NV097_SET_TEXGEN_Q) / 16; unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q : NV_PGRAPH_CSV1_A_T0_Q; PG_SET_MASK(reg, mask, kelvin_map_texgen(parameter, 3)); } DEF_METHOD_INC(NV097, SET_TEXTURE_MATRIX_ENABLE) { int slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4; pg->texture_matrix_enable[slot] = parameter; } DEF_METHOD(NV097, SET_POINT_SIZE) { PG_SET_MASK(NV_PGRAPH_POINTSIZE, NV097_SET_POINT_SIZE_V, parameter); } DEF_METHOD_INC(NV097, SET_PROJECTION_MATRIX) { int slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; // pg->projection_matrix[slot] = *(float*)¶meter; unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot/4; pg->vsh_constants[row][slot%4] = parameter; pg->vsh_constants_dirty[row] = true; } DEF_METHOD_INC(NV097, SET_MODEL_VIEW_MATRIX) { int slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; unsigned int matnum = slot / 16; unsigned int entry = slot % 16; unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum*8 + entry/4; pg->vsh_constants[row][entry % 4] = parameter; pg->vsh_constants_dirty[row] = true; } DEF_METHOD_INC(NV097, SET_INVERSE_MODEL_VIEW_MATRIX) { int slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; unsigned int matnum = slot / 16; unsigned int entry = slot % 16; unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum*8 + entry/4; pg->vsh_constants[row][entry % 4] = parameter; pg->vsh_constants_dirty[row] = true; } DEF_METHOD_INC(NV097, SET_COMPOSITE_MATRIX) { int slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot/4; pg->vsh_constants[row][slot%4] = parameter; pg->vsh_constants_dirty[row] = true; } DEF_METHOD_INC(NV097, SET_TEXTURE_MATRIX) { int slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; unsigned int tex = slot / 16; unsigned int entry = slot % 16; unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex*8 + entry/4; pg->vsh_constants[row][entry%4] = parameter; pg->vsh_constants_dirty[row] = true; } DEF_METHOD_INC(NV097, SET_FOG_PARAMS) { int slot = (method - NV097_SET_FOG_PARAMS) / 4; if (slot < 2) { pgraph_reg_w(pg, NV_PGRAPH_FOGPARAM0 + slot*4, parameter); } else { /* FIXME: No idea where slot = 2 is */ } pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; } /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ DEF_METHOD_INC(NV097, SET_TEXGEN_PLANE_S) { int slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; unsigned int tex = slot / 16; unsigned int entry = slot % 16; unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex*8 + entry/4; pg->vsh_constants[row][entry%4] = parameter; pg->vsh_constants_dirty[row] = true; } DEF_METHOD(NV097, SET_TEXGEN_VIEW_MODEL) { PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_TEXGEN_REF, parameter); } DEF_METHOD_INC(NV097, SET_FOG_PLANE) { int slot = (method - NV097_SET_FOG_PLANE) / 4; pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; } DEF_METHOD_INC(NV097, SET_SCENE_AMBIENT_COLOR) { int slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; // ?? pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; } DEF_METHOD_INC(NV097, SET_VIEWPORT_OFFSET) { int slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; } DEF_METHOD_INC(NV097, SET_POINT_PARAMS) { int slot = (method - NV097_SET_POINT_PARAMS) / 4; pg->point_params[slot] = *(float *)¶meter; /* FIXME: Where? */ } DEF_METHOD_INC(NV097, SET_EYE_POSITION) { int slot = (method - NV097_SET_EYE_POSITION) / 4; pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; } DEF_METHOD_INC(NV097, SET_COMBINER_FACTOR0) { int slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; pgraph_reg_w(pg, NV_PGRAPH_COMBINEFACTOR0 + slot*4, parameter); } DEF_METHOD_INC(NV097, SET_COMBINER_FACTOR1) { int slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; pgraph_reg_w(pg, NV_PGRAPH_COMBINEFACTOR1 + slot*4, parameter); } DEF_METHOD_INC(NV097, SET_COMBINER_ALPHA_OCW) { int slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; pgraph_reg_w(pg, NV_PGRAPH_COMBINEALPHAO0 + slot*4, parameter); } DEF_METHOD_INC(NV097, SET_COMBINER_COLOR_ICW) { int slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; pgraph_reg_w(pg, NV_PGRAPH_COMBINECOLORI0 + slot*4, parameter); } DEF_METHOD_INC(NV097, SET_VIEWPORT_SCALE) { int slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; } DEF_METHOD_INC(NV097, SET_TRANSFORM_PROGRAM) { int slot = (method - NV097_SET_TRANSFORM_PROGRAM) / 4; int program_load = PG_GET_MASK(NV_PGRAPH_CHEOPS_OFFSET, NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR); assert(program_load < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); pg->program_data[program_load][slot%4] = parameter; pg->program_data_dirty = true; if (slot % 4 == 3) { PG_SET_MASK(NV_PGRAPH_CHEOPS_OFFSET, NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, program_load+1); } } DEF_METHOD_INC(NV097, SET_TRANSFORM_CONSTANT) { int slot = (method - NV097_SET_TRANSFORM_CONSTANT) / 4; int const_load = PG_GET_MASK(NV_PGRAPH_CHEOPS_OFFSET, NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR); assert(const_load < NV2A_VERTEXSHADER_CONSTANTS); // VertexShaderConstant *constant = &pg->constants[const_load]; pg->vsh_constants_dirty[const_load] |= (parameter != pg->vsh_constants[const_load][slot%4]); pg->vsh_constants[const_load][slot%4] = parameter; if (slot % 4 == 3) { PG_SET_MASK(NV_PGRAPH_CHEOPS_OFFSET, NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, const_load+1); } } DEF_METHOD_INC(NV097, SET_VERTEX3F) { int slot = (method - NV097_SET_VERTEX3F) / 4; VertexAttribute *attribute = &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); attribute->inline_value[slot] = *(float*)¶meter; attribute->inline_value[3] = 1.0f; if (slot == 2) { pgraph_finish_inline_buffer_vertex(pg); } } /* Handles NV097_SET_BACK_LIGHT_* */ DEF_METHOD_INC(NV097, SET_BACK_LIGHT_AMBIENT_COLOR) { int slot = (method - NV097_SET_BACK_LIGHT_AMBIENT_COLOR) / 4; unsigned int part = NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4 + slot % 16; slot /= 16; /* [Light index] */ assert(slot < 8); switch(part * 4) { case NV097_SET_BACK_LIGHT_AMBIENT_COLOR ... NV097_SET_BACK_LIGHT_AMBIENT_COLOR + 8: part -= NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4; pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6][part] = parameter; pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6] = true; break; case NV097_SET_BACK_LIGHT_DIFFUSE_COLOR ... NV097_SET_BACK_LIGHT_DIFFUSE_COLOR + 8: part -= NV097_SET_BACK_LIGHT_DIFFUSE_COLOR / 4; pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6][part] = parameter; pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6] = true; break; case NV097_SET_BACK_LIGHT_SPECULAR_COLOR ... NV097_SET_BACK_LIGHT_SPECULAR_COLOR + 8: part -= NV097_SET_BACK_LIGHT_SPECULAR_COLOR / 4; pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6][part] = parameter; pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6] = true; break; default: assert(false); break; } } /* Handles all the light source props except for NV097_SET_BACK_LIGHT_* */ DEF_METHOD_INC(NV097, SET_LIGHT_AMBIENT_COLOR) { int slot = (method - NV097_SET_LIGHT_AMBIENT_COLOR) / 4; unsigned int part = NV097_SET_LIGHT_AMBIENT_COLOR / 4 + slot % 32; slot /= 32; /* [Light index] */ assert(slot < 8); switch(part * 4) { case NV097_SET_LIGHT_AMBIENT_COLOR ... NV097_SET_LIGHT_AMBIENT_COLOR + 8: part -= NV097_SET_LIGHT_AMBIENT_COLOR / 4; pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6][part] = parameter; pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6] = true; break; case NV097_SET_LIGHT_DIFFUSE_COLOR ... NV097_SET_LIGHT_DIFFUSE_COLOR + 8: part -= NV097_SET_LIGHT_DIFFUSE_COLOR / 4; pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6][part] = parameter; pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6] = true; break; case NV097_SET_LIGHT_SPECULAR_COLOR ... NV097_SET_LIGHT_SPECULAR_COLOR + 8: part -= NV097_SET_LIGHT_SPECULAR_COLOR / 4; pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6][part] = parameter; pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6] = true; break; case NV097_SET_LIGHT_LOCAL_RANGE: pg->ltc1[NV_IGRAPH_XF_LTC1_r0 + slot][0] = parameter; pg->ltc1_dirty[NV_IGRAPH_XF_LTC1_r0 + slot] = true; break; case NV097_SET_LIGHT_INFINITE_HALF_VECTOR ... NV097_SET_LIGHT_INFINITE_HALF_VECTOR + 8: part -= NV097_SET_LIGHT_INFINITE_HALF_VECTOR / 4; pg->light_infinite_half_vector[slot][part] = *(float*)¶meter; break; case NV097_SET_LIGHT_INFINITE_DIRECTION ... NV097_SET_LIGHT_INFINITE_DIRECTION + 8: part -= NV097_SET_LIGHT_INFINITE_DIRECTION / 4; pg->light_infinite_direction[slot][part] = *(float*)¶meter; break; case NV097_SET_LIGHT_SPOT_FALLOFF ... NV097_SET_LIGHT_SPOT_FALLOFF + 8: part -= NV097_SET_LIGHT_SPOT_FALLOFF / 4; pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2][part] = parameter; pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2] = true; break; case NV097_SET_LIGHT_SPOT_DIRECTION ... NV097_SET_LIGHT_SPOT_DIRECTION + 12: part -= NV097_SET_LIGHT_SPOT_DIRECTION / 4; pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2][part] = parameter; pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2] = true; break; case NV097_SET_LIGHT_LOCAL_POSITION ... NV097_SET_LIGHT_LOCAL_POSITION + 8: part -= NV097_SET_LIGHT_LOCAL_POSITION / 4; pg->light_local_position[slot][part] = *(float*)¶meter; break; case NV097_SET_LIGHT_LOCAL_ATTENUATION ... NV097_SET_LIGHT_LOCAL_ATTENUATION + 8: part -= NV097_SET_LIGHT_LOCAL_ATTENUATION / 4; pg->light_local_attenuation[slot][part] = *(float*)¶meter; break; default: assert(false); break; } } DEF_METHOD_INC(NV097, SET_VERTEX4F) { int slot = (method - NV097_SET_VERTEX4F) / 4; VertexAttribute *attribute = &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); attribute->inline_value[slot] = *(float*)¶meter; if (slot == 3) { pgraph_finish_inline_buffer_vertex(pg); } } DEF_METHOD_INC(NV097, SET_NORMAL3S) { int slot = (method - NV097_SET_NORMAL3S) / 4; unsigned int part = slot % 2; VertexAttribute *attribute = &pg->vertex_attributes[NV2A_VERTEX_ATTR_NORMAL]; pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_NORMAL); int16_t val = parameter & 0xFFFF; attribute->inline_value[part * 2 + 0] = MAX(-1.0f, (float)val / 32767.0f); val = parameter >> 16; attribute->inline_value[part * 2 + 1] = MAX(-1.0f, (float)val / 32767.0f); } #define SET_VERTEX_ATTRIBUTE_4S(command, attr_index) \ do { \ int slot = (method - (command)) / 4; \ unsigned int part = slot % 2; \ VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \ pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \ attribute->inline_value[part * 2 + 0] = \ (float)(int16_t)(parameter & 0xFFFF); \ attribute->inline_value[part * 2 + 1] = \ (float)(int16_t)(parameter >> 16); \ } while (0) DEF_METHOD_INC(NV097, SET_TEXCOORD0_4S) { SET_VERTEX_ATTRIBUTE_4S(NV097_SET_TEXCOORD0_4S, NV2A_VERTEX_ATTR_TEXTURE0); } DEF_METHOD_INC(NV097, SET_TEXCOORD1_4S) { SET_VERTEX_ATTRIBUTE_4S(NV097_SET_TEXCOORD1_4S, NV2A_VERTEX_ATTR_TEXTURE1); } DEF_METHOD_INC(NV097, SET_TEXCOORD2_4S) { SET_VERTEX_ATTRIBUTE_4S(NV097_SET_TEXCOORD2_4S, NV2A_VERTEX_ATTR_TEXTURE2); } DEF_METHOD_INC(NV097, SET_TEXCOORD3_4S) { SET_VERTEX_ATTRIBUTE_4S(NV097_SET_TEXCOORD3_4S, NV2A_VERTEX_ATTR_TEXTURE3); } #undef SET_VERTEX_ATTRIBUTE_4S #define SET_VERTEX_ATRIBUTE_TEX_2S(attr_index) \ do { \ VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \ pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \ attribute->inline_value[0] = (float)(int16_t)(parameter & 0xFFFF); \ attribute->inline_value[1] = (float)(int16_t)(parameter >> 16); \ attribute->inline_value[2] = 0.0f; \ attribute->inline_value[3] = 1.0f; \ } while (0) DEF_METHOD_INC(NV097, SET_TEXCOORD0_2S) { SET_VERTEX_ATRIBUTE_TEX_2S(NV2A_VERTEX_ATTR_TEXTURE0); } DEF_METHOD_INC(NV097, SET_TEXCOORD1_2S) { SET_VERTEX_ATRIBUTE_TEX_2S(NV2A_VERTEX_ATTR_TEXTURE1); } DEF_METHOD_INC(NV097, SET_TEXCOORD2_2S) { SET_VERTEX_ATRIBUTE_TEX_2S(NV2A_VERTEX_ATTR_TEXTURE2); } DEF_METHOD_INC(NV097, SET_TEXCOORD3_2S) { SET_VERTEX_ATRIBUTE_TEX_2S(NV2A_VERTEX_ATTR_TEXTURE3); } #undef SET_VERTEX_ATRIBUTE_TEX_2S #define SET_VERTEX_COLOR_3F(command, attr_index) \ do { \ int slot = (method - (command)) / 4; \ VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \ pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \ attribute->inline_value[slot] = *(float*)¶meter; \ attribute->inline_value[3] = 1.0f; \ } while (0) DEF_METHOD_INC(NV097, SET_DIFFUSE_COLOR3F) { SET_VERTEX_COLOR_3F(NV097_SET_DIFFUSE_COLOR3F, NV2A_VERTEX_ATTR_DIFFUSE); } DEF_METHOD_INC(NV097, SET_SPECULAR_COLOR3F) { SET_VERTEX_COLOR_3F(NV097_SET_SPECULAR_COLOR3F, NV2A_VERTEX_ATTR_SPECULAR); } #undef SET_VERTEX_COLOR_3F #define SET_VERTEX_ATTRIBUTE_F(command, attr_index) \ do { \ int slot = (method - (command)) / 4; \ VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \ pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \ attribute->inline_value[slot] = *(float*)¶meter; \ } while (0) DEF_METHOD_INC(NV097, SET_NORMAL3F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_NORMAL3F, NV2A_VERTEX_ATTR_NORMAL); } DEF_METHOD_INC(NV097, SET_DIFFUSE_COLOR4F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_DIFFUSE_COLOR4F, NV2A_VERTEX_ATTR_DIFFUSE); } DEF_METHOD_INC(NV097, SET_SPECULAR_COLOR4F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_SPECULAR_COLOR4F, NV2A_VERTEX_ATTR_SPECULAR); } DEF_METHOD_INC(NV097, SET_TEXCOORD0_4F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD0_4F, NV2A_VERTEX_ATTR_TEXTURE0); } DEF_METHOD_INC(NV097, SET_TEXCOORD1_4F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD1_4F, NV2A_VERTEX_ATTR_TEXTURE1); } DEF_METHOD_INC(NV097, SET_TEXCOORD2_4F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD2_4F, NV2A_VERTEX_ATTR_TEXTURE2); } DEF_METHOD_INC(NV097, SET_TEXCOORD3_4F) { SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD3_4F, NV2A_VERTEX_ATTR_TEXTURE3); } #undef SET_VERTEX_ATTRIBUTE_F #define SET_VERTEX_ATRIBUTE_TEX_2F(command, attr_index) \ do { \ int slot = (method - (command)) / 4; \ VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \ pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \ attribute->inline_value[slot] = *(float*)¶meter; \ attribute->inline_value[2] = 0.0f; \ attribute->inline_value[3] = 1.0f; \ } while (0) DEF_METHOD_INC(NV097, SET_TEXCOORD0_2F) { SET_VERTEX_ATRIBUTE_TEX_2F(NV097_SET_TEXCOORD0_2F, NV2A_VERTEX_ATTR_TEXTURE0); } DEF_METHOD_INC(NV097, SET_TEXCOORD1_2F) { SET_VERTEX_ATRIBUTE_TEX_2F(NV097_SET_TEXCOORD1_2F, NV2A_VERTEX_ATTR_TEXTURE1); } DEF_METHOD_INC(NV097, SET_TEXCOORD2_2F) { SET_VERTEX_ATRIBUTE_TEX_2F(NV097_SET_TEXCOORD2_2F, NV2A_VERTEX_ATTR_TEXTURE2); } DEF_METHOD_INC(NV097, SET_TEXCOORD3_2F) { SET_VERTEX_ATRIBUTE_TEX_2F(NV097_SET_TEXCOORD3_2F, NV2A_VERTEX_ATTR_TEXTURE3); } #undef SET_VERTEX_ATRIBUTE_TEX_2F #define SET_VERTEX_ATTRIBUTE_4UB(command, attr_index) \ do { \ VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \ pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \ attribute->inline_value[0] = (parameter & 0xFF) / 255.0f; \ attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f; \ attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f; \ attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f; \ } while (0) DEF_METHOD_INC(NV097, SET_DIFFUSE_COLOR4UB) { SET_VERTEX_ATTRIBUTE_4UB(NV097_SET_DIFFUSE_COLOR4UB, NV2A_VERTEX_ATTR_DIFFUSE); } DEF_METHOD_INC(NV097, SET_SPECULAR_COLOR4UB) { SET_VERTEX_ATTRIBUTE_4UB(NV097_SET_SPECULAR_COLOR4UB, NV2A_VERTEX_ATTR_SPECULAR); } #undef SET_VERTEX_ATTRIBUTE_4UB DEF_METHOD_INC(NV097, SET_VERTEX_DATA_ARRAY_FORMAT) { int slot = (method - NV097_SET_VERTEX_DATA_ARRAY_FORMAT) / 4; VertexAttribute *attr = &pg->vertex_attributes[slot]; attr->format = GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE); attr->count = GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE); attr->stride = GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE); NV2A_DPRINTF("vertex data array format=%d, count=%d, stride=%d\n", attr->format, attr->count, attr->stride); switch (attr->format) { case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D: attr->size = 1; assert(attr->count == 4); break; case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL: attr->size = 1; break; case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1: attr->size = 2; break; case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F: attr->size = 4; break; case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K: attr->size = 2; break; case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: /* 3 signed, normalized components packed in 32-bits. (11,11,10) */ attr->size = 4; assert(attr->count == 1); break; default: fprintf(stderr, "Unknown vertex type: 0x%x\n", attr->format); assert(false); break; } if (attr->format == NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP) { pg->compressed_attrs |= (1 << slot); } else { pg->compressed_attrs &= ~(1 << slot); } } DEF_METHOD_INC(NV097, SET_VERTEX_DATA_ARRAY_OFFSET) { int slot = (method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4; pg->vertex_attributes[slot].dma_select = parameter & 0x80000000; pg->vertex_attributes[slot].offset = parameter & 0x7fffffff; } DEF_METHOD(NV097, SET_LOGIC_OP_ENABLE) { PG_SET_MASK(NV_PGRAPH_BLEND, NV_PGRAPH_BLEND_LOGICOP_ENABLE, parameter); } DEF_METHOD(NV097, SET_LOGIC_OP) { PG_SET_MASK(NV_PGRAPH_BLEND, NV_PGRAPH_BLEND_LOGICOP, parameter & 0xF); } DEF_METHOD(NV097, CLEAR_REPORT_VALUE) { d->pgraph.renderer->ops.clear_report_value(d); } DEF_METHOD(NV097, SET_ZPASS_PIXEL_COUNT_ENABLE) { pg->zpass_pixel_count_enable = parameter; } DEF_METHOD(NV097, GET_REPORT) { uint8_t type = GET_MASK(parameter, NV097_GET_REPORT_TYPE); assert(type == NV097_GET_REPORT_TYPE_ZPASS_PIXEL_CNT); d->pgraph.renderer->ops.get_report(d, parameter); } DEF_METHOD_INC(NV097, SET_EYE_DIRECTION) { int slot = (method - NV097_SET_EYE_DIRECTION) / 4; pg->ltctxa[NV_IGRAPH_XF_LTCTXA_EYED][slot] = parameter; pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_EYED] = true; } DEF_METHOD(NV097, SET_BEGIN_END) { if (parameter == NV097_SET_BEGIN_END_OP_END) { if (pg->primitive_mode == PRIM_TYPE_INVALID) { NV2A_DPRINTF("End without Begin!\n"); } nv2a_profile_inc_counter(NV2A_PROF_BEGIN_ENDS); d->pgraph.renderer->ops.draw_end(d); pgraph_reset_inline_buffers(pg); pg->primitive_mode = PRIM_TYPE_INVALID; } else { if (pg->primitive_mode != PRIM_TYPE_INVALID) { NV2A_DPRINTF("Begin without End!\n"); } assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON); pg->primitive_mode = parameter; pgraph_reset_inline_buffers(pg); d->pgraph.renderer->ops.draw_begin(d); } } DEF_METHOD(NV097, SET_TEXTURE_OFFSET) { int slot = (method - NV097_SET_TEXTURE_OFFSET) / 64; pgraph_reg_w(pg, NV_PGRAPH_TEXOFFSET0 + slot * 4, parameter); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_FORMAT) { int slot = (method - NV097_SET_TEXTURE_FORMAT) / 64; bool dma_select = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CONTEXT_DMA) == 2; bool cubemap = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CUBEMAP_ENABLE); unsigned int border_source = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE); unsigned int dimensionality = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_DIMENSIONALITY); unsigned int color_format = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_COLOR); unsigned int levels = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_MIPMAP_LEVELS); unsigned int log_width = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_U); unsigned int log_height = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_V); unsigned int log_depth = GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P); unsigned int reg = NV_PGRAPH_TEXFMT0 + slot * 4; PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_CONTEXT_DMA, dma_select); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE, cubemap); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_BORDER_SOURCE, border_source); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_DIMENSIONALITY, dimensionality); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_COLOR, color_format); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS, levels); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_U, log_width); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_V, log_height); PG_SET_MASK(reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_P, log_depth); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_CONTROL0) { int slot = (method - NV097_SET_TEXTURE_CONTROL0) / 64; pgraph_reg_w(pg, NV_PGRAPH_TEXCTL0_0 + slot*4, parameter); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_CONTROL1) { int slot = (method - NV097_SET_TEXTURE_CONTROL1) / 64; pgraph_reg_w(pg, NV_PGRAPH_TEXCTL1_0 + slot*4, parameter); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_FILTER) { int slot = (method - NV097_SET_TEXTURE_FILTER) / 64; pgraph_reg_w(pg, NV_PGRAPH_TEXFILTER0 + slot * 4, parameter); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_IMAGE_RECT) { int slot = (method - NV097_SET_TEXTURE_IMAGE_RECT) / 64; pgraph_reg_w(pg, NV_PGRAPH_TEXIMAGERECT0 + slot * 4, parameter); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_PALETTE) { int slot = (method - NV097_SET_TEXTURE_PALETTE) / 64; bool dma_select = GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_CONTEXT_DMA) == 1; unsigned int length = GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_LENGTH); unsigned int offset = GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_OFFSET); unsigned int reg = NV_PGRAPH_TEXPALETTE0 + slot * 4; PG_SET_MASK(reg, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA, dma_select); PG_SET_MASK(reg, NV_PGRAPH_TEXPALETTE0_LENGTH, length); PG_SET_MASK(reg, NV_PGRAPH_TEXPALETTE0_OFFSET, offset); pg->texture_dirty[slot] = true; } DEF_METHOD(NV097, SET_TEXTURE_BORDER_COLOR) { int slot = (method - NV097_SET_TEXTURE_BORDER_COLOR) / 64; pgraph_reg_w(pg, NV_PGRAPH_BORDERCOLOR0 + slot * 4, parameter); } DEF_METHOD(NV097, SET_TEXTURE_SET_BUMP_ENV_MAT) { int slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_MAT) / 4; if (slot < 16) { /* discard */ return; } slot -= 16; const int swizzle[4] = { NV_PGRAPH_BUMPMAT00, NV_PGRAPH_BUMPMAT01, NV_PGRAPH_BUMPMAT11, NV_PGRAPH_BUMPMAT10 }; pgraph_reg_w(pg, swizzle[slot % 4] + slot / 4, parameter); } DEF_METHOD(NV097, SET_TEXTURE_SET_BUMP_ENV_SCALE) { int slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE) / 64; if (slot == 0) { /* discard */ return; } slot--; pgraph_reg_w(pg, NV_PGRAPH_BUMPSCALE1 + slot * 4, parameter); } DEF_METHOD(NV097, SET_TEXTURE_SET_BUMP_ENV_OFFSET) { int slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET) / 64; if (slot == 0) { /* discard */ return; } slot--; pgraph_reg_w(pg, NV_PGRAPH_BUMPOFFSET1 + slot * 4, parameter); } static void pgraph_expand_draw_arrays(NV2AState *d) { PGRAPHState *pg = &d->pgraph; uint32_t start = pg->draw_arrays_start[pg->draw_arrays_length - 1]; uint32_t count = pg->draw_arrays_count[pg->draw_arrays_length - 1]; /* Render any previously squashed DRAW_ARRAYS calls. This case would be * triggered if a set of BEGIN+DA+END triplets is followed by the * BEGIN+DA+ARRAY_ELEMENT+... chain that caused this expansion. */ if (pg->draw_arrays_length > 1) { d->pgraph.renderer->ops.flush_draw(d); pgraph_reset_inline_buffers(pg); } assert((pg->inline_elements_length + count) < NV2A_MAX_BATCH_LENGTH); for (unsigned int i = 0; i < count; i++) { pg->inline_elements[pg->inline_elements_length++] = start + i; } pgraph_reset_draw_arrays(pg); } void pgraph_check_within_begin_end_block(PGRAPHState *pg) { if (pg->primitive_mode == PRIM_TYPE_INVALID) { NV2A_DPRINTF("Vertex data being sent outside of begin/end block!\n"); } } DEF_METHOD_NON_INC(NV097, ARRAY_ELEMENT16) { pgraph_check_within_begin_end_block(pg); if (pg->draw_arrays_length) { pgraph_expand_draw_arrays(d); } assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); pg->inline_elements[pg->inline_elements_length++] = parameter & 0xFFFF; pg->inline_elements[pg->inline_elements_length++] = parameter >> 16; } DEF_METHOD_NON_INC(NV097, ARRAY_ELEMENT32) { pgraph_check_within_begin_end_block(pg); if (pg->draw_arrays_length) { pgraph_expand_draw_arrays(d); } assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); pg->inline_elements[pg->inline_elements_length++] = parameter; } DEF_METHOD(NV097, DRAW_ARRAYS) { pgraph_check_within_begin_end_block(pg); int32_t start = GET_MASK(parameter, NV097_DRAW_ARRAYS_START_INDEX); int32_t count = GET_MASK(parameter, NV097_DRAW_ARRAYS_COUNT) + 1; if (pg->inline_elements_length) { /* FIXME: Determine HW behavior for overflow case. */ assert((pg->inline_elements_length + count) < NV2A_MAX_BATCH_LENGTH); assert(!pg->draw_arrays_prevent_connect); for (unsigned int i = 0; i < count; i++) { pg->inline_elements[pg->inline_elements_length++] = start + i; } return; } pg->draw_arrays_min_start = MIN(pg->draw_arrays_min_start, start); pg->draw_arrays_max_count = MAX(pg->draw_arrays_max_count, start + count); assert(pg->draw_arrays_length < ARRAY_SIZE(pg->draw_arrays_start)); /* Attempt to connect contiguous primitives */ if (!pg->draw_arrays_prevent_connect && pg->draw_arrays_length > 0) { unsigned int last_start = pg->draw_arrays_start[pg->draw_arrays_length - 1]; int32_t *last_count = &pg->draw_arrays_count[pg->draw_arrays_length - 1]; if (start == (last_start + *last_count)) { *last_count += count; return; } } pg->draw_arrays_start[pg->draw_arrays_length] = start; pg->draw_arrays_count[pg->draw_arrays_length] = count; pg->draw_arrays_length++; pg->draw_arrays_prevent_connect = false; } DEF_METHOD_NON_INC(NV097, INLINE_ARRAY) { pgraph_check_within_begin_end_block(pg); assert(pg->inline_array_length < NV2A_MAX_BATCH_LENGTH); pg->inline_array[pg->inline_array_length++] = parameter; } DEF_METHOD_INC(NV097, SET_EYE_VECTOR) { int slot = (method - NV097_SET_EYE_VECTOR) / 4; pgraph_reg_w(pg, NV_PGRAPH_EYEVEC0 + slot * 4, parameter); } DEF_METHOD_INC(NV097, SET_VERTEX_DATA2F_M) { int slot = (method - NV097_SET_VERTEX_DATA2F_M) / 4; unsigned int part = slot % 2; slot /= 2; VertexAttribute *attribute = &pg->vertex_attributes[slot]; pgraph_allocate_inline_buffer_vertices(pg, slot); attribute->inline_value[part] = *(float*)¶meter; /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ attribute->inline_value[2] = 0.0; attribute->inline_value[3] = 1.0; if ((slot == 0) && (part == 1)) { pgraph_finish_inline_buffer_vertex(pg); } } DEF_METHOD_INC(NV097, SET_VERTEX_DATA4F_M) { int slot = (method - NV097_SET_VERTEX_DATA4F_M) / 4; unsigned int part = slot % 4; slot /= 4; VertexAttribute *attribute = &pg->vertex_attributes[slot]; pgraph_allocate_inline_buffer_vertices(pg, slot); attribute->inline_value[part] = *(float*)¶meter; if ((slot == 0) && (part == 3)) { pgraph_finish_inline_buffer_vertex(pg); } } DEF_METHOD_INC(NV097, SET_VERTEX_DATA2S) { int slot = (method - NV097_SET_VERTEX_DATA2S) / 4; VertexAttribute *attribute = &pg->vertex_attributes[slot]; pgraph_allocate_inline_buffer_vertices(pg, slot); attribute->inline_value[0] = (float)(int16_t)(parameter & 0xFFFF); attribute->inline_value[1] = (float)(int16_t)(parameter >> 16); attribute->inline_value[2] = 0.0; attribute->inline_value[3] = 1.0; if (slot == 0) { pgraph_finish_inline_buffer_vertex(pg); } } DEF_METHOD_INC(NV097, SET_VERTEX_DATA4UB) { int slot = (method - NV097_SET_VERTEX_DATA4UB) / 4; VertexAttribute *attribute = &pg->vertex_attributes[slot]; pgraph_allocate_inline_buffer_vertices(pg, slot); attribute->inline_value[0] = (parameter & 0xFF) / 255.0; attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0; attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0; attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0; if (slot == 0) { pgraph_finish_inline_buffer_vertex(pg); } } DEF_METHOD_INC(NV097, SET_VERTEX_DATA4S_M) { int slot = (method - NV097_SET_VERTEX_DATA4S_M) / 4; unsigned int part = slot % 2; slot /= 2; VertexAttribute *attribute = &pg->vertex_attributes[slot]; pgraph_allocate_inline_buffer_vertices(pg, slot); attribute->inline_value[part * 2 + 0] = (float)(int16_t)(parameter & 0xFFFF); attribute->inline_value[part * 2 + 1] = (float)(int16_t)(parameter >> 16); if ((slot == 0) && (part == 1)) { pgraph_finish_inline_buffer_vertex(pg); } } DEF_METHOD(NV097, SET_SEMAPHORE_OFFSET) { pgraph_reg_w(pg, NV_PGRAPH_SEMAPHOREOFFSET, parameter); } DEF_METHOD(NV097, BACK_END_WRITE_SEMAPHORE_RELEASE) { d->pgraph.renderer->ops.surface_update(d, false, true, true); //qemu_mutex_unlock(&d->pgraph.lock); //qemu_mutex_lock_iothread(); uint32_t semaphore_offset = pgraph_reg_r(pg, NV_PGRAPH_SEMAPHOREOFFSET); hwaddr semaphore_dma_len; uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, pg->dma_semaphore, &semaphore_dma_len); assert(semaphore_offset < semaphore_dma_len); semaphore_data += semaphore_offset; stl_le_p((uint32_t*)semaphore_data, parameter); //qemu_mutex_lock(&d->pgraph.lock); //qemu_mutex_unlock_iothread(); } DEF_METHOD(NV097, SET_ZMIN_MAX_CONTROL) { switch (GET_MASK(parameter, NV097_SET_ZMIN_MAX_CONTROL_ZCLAMP_EN)) { case NV097_SET_ZMIN_MAX_CONTROL_ZCLAMP_EN_CULL: PG_SET_MASK(NV_PGRAPH_ZCOMPRESSOCCLUDE, NV_PGRAPH_ZCOMPRESSOCCLUDE_ZCLAMP_EN, NV_PGRAPH_ZCOMPRESSOCCLUDE_ZCLAMP_EN_CULL); break; case NV097_SET_ZMIN_MAX_CONTROL_ZCLAMP_EN_CLAMP: PG_SET_MASK(NV_PGRAPH_ZCOMPRESSOCCLUDE, NV_PGRAPH_ZCOMPRESSOCCLUDE_ZCLAMP_EN, NV_PGRAPH_ZCOMPRESSOCCLUDE_ZCLAMP_EN_CLAMP); break; default: /* FIXME: Should raise NV_PGRAPH_NSOURCE_DATA_ERROR_PENDING */ assert(!"Invalid zclamp value"); break; } } DEF_METHOD(NV097, SET_ANTI_ALIASING_CONTROL) { PG_SET_MASK(NV_PGRAPH_ANTIALIASING, NV_PGRAPH_ANTIALIASING_ENABLE, GET_MASK(parameter, NV097_SET_ANTI_ALIASING_CONTROL_ENABLE)); // FIXME: Handle the remaining bits (observed values 0xFFFF0000, 0xFFFF0001) } DEF_METHOD(NV097, SET_ZSTENCIL_CLEAR_VALUE) { pgraph_reg_w(pg, NV_PGRAPH_ZSTENCILCLEARVALUE, parameter); } DEF_METHOD(NV097, SET_COLOR_CLEAR_VALUE) { pgraph_reg_w(pg, NV_PGRAPH_COLORCLEARVALUE, parameter); } DEF_METHOD(NV097, CLEAR_SURFACE) { d->pgraph.renderer->ops.clear_surface(d, parameter); } DEF_METHOD(NV097, SET_CLEAR_RECT_HORIZONTAL) { pgraph_reg_w(pg, NV_PGRAPH_CLEARRECTX, parameter); } DEF_METHOD(NV097, SET_CLEAR_RECT_VERTICAL) { pgraph_reg_w(pg, NV_PGRAPH_CLEARRECTY, parameter); } DEF_METHOD_INC(NV097, SET_SPECULAR_FOG_FACTOR) { int slot = (method - NV097_SET_SPECULAR_FOG_FACTOR) / 4; pgraph_reg_w(pg, NV_PGRAPH_SPECFOGFACTOR0 + slot*4, parameter); } DEF_METHOD(NV097, SET_SHADER_CLIP_PLANE_MODE) { pgraph_reg_w(pg, NV_PGRAPH_SHADERCLIPMODE, parameter); } DEF_METHOD_INC(NV097, SET_COMBINER_COLOR_OCW) { int slot = (method - NV097_SET_COMBINER_COLOR_OCW) / 4; pgraph_reg_w(pg, NV_PGRAPH_COMBINECOLORO0 + slot*4, parameter); } DEF_METHOD(NV097, SET_COMBINER_CONTROL) { pgraph_reg_w(pg, NV_PGRAPH_COMBINECTL, parameter); } DEF_METHOD(NV097, SET_SHADOW_ZSLOPE_THRESHOLD) { pgraph_reg_w(pg, NV_PGRAPH_SHADOWZSLOPETHRESHOLD, parameter); assert(parameter == 0x7F800000); /* FIXME: Unimplemented */ } DEF_METHOD(NV097, SET_SHADOW_DEPTH_FUNC) { PG_SET_MASK(NV_PGRAPH_SHADOWCTL, NV_PGRAPH_SHADOWCTL_SHADOW_ZFUNC, parameter); } DEF_METHOD(NV097, SET_SHADER_STAGE_PROGRAM) { pgraph_reg_w(pg, NV_PGRAPH_SHADERPROG, parameter); } DEF_METHOD(NV097, SET_DOT_RGBMAPPING) { PG_SET_MASK(NV_PGRAPH_SHADERCTL, 0xFFF, GET_MASK(parameter, 0xFFF)); } DEF_METHOD(NV097, SET_SHADER_OTHER_STAGE_INPUT) { PG_SET_MASK(NV_PGRAPH_SHADERCTL, 0xFFFF000, GET_MASK(parameter, 0xFFFF000)); } DEF_METHOD_INC(NV097, SET_TRANSFORM_DATA) { int slot = (method - NV097_SET_TRANSFORM_DATA) / 4; pg->vertex_state_shader_v0[slot] = parameter; } DEF_METHOD(NV097, LAUNCH_TRANSFORM_PROGRAM) { unsigned int program_start = parameter; assert(program_start < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); Nv2aVshProgram program; Nv2aVshParseResult result = nv2a_vsh_parse_program( &program, pg->program_data[program_start], NV2A_MAX_TRANSFORM_PROGRAM_LENGTH - program_start); assert(result == NV2AVPR_SUCCESS); Nv2aVshCPUXVSSExecutionState state_linkage; Nv2aVshExecutionState state = nv2a_vsh_emu_initialize_xss_execution_state( &state_linkage, (float*)pg->vsh_constants); memcpy(state_linkage.input_regs, pg->vertex_state_shader_v0, sizeof(pg->vertex_state_shader_v0)); nv2a_vsh_emu_execute_track_context_writes(&state, &program, pg->vsh_constants_dirty); nv2a_vsh_program_destroy(&program); } DEF_METHOD(NV097, SET_TRANSFORM_EXECUTION_MODE) { PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_MODE, GET_MASK(parameter, NV097_SET_TRANSFORM_EXECUTION_MODE_MODE)); PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_RANGE_MODE, GET_MASK(parameter, NV097_SET_TRANSFORM_EXECUTION_MODE_RANGE_MODE)); } DEF_METHOD(NV097, SET_TRANSFORM_PROGRAM_CXT_WRITE_EN) { pg->enable_vertex_program_write = parameter; } DEF_METHOD(NV097, SET_TRANSFORM_PROGRAM_LOAD) { assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); PG_SET_MASK(NV_PGRAPH_CHEOPS_OFFSET, NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, parameter); } DEF_METHOD(NV097, SET_TRANSFORM_PROGRAM_START) { assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START, parameter); } DEF_METHOD(NV097, SET_TRANSFORM_CONSTANT_LOAD) { assert(parameter < NV2A_VERTEXSHADER_CONSTANTS); PG_SET_MASK(NV_PGRAPH_CHEOPS_OFFSET, NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, parameter); } void pgraph_get_clear_color(PGRAPHState *pg, float rgba[4]) { uint32_t clear_color = pgraph_reg_r(pg, NV_PGRAPH_COLORCLEARVALUE); float *r = &rgba[0], *g = &rgba[1], *b = &rgba[2], *a = &rgba[3]; /* Handle RGB */ switch(pg->surface_shape.color_format) { case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5: case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5: *r = ((clear_color >> 10) & 0x1F) / 31.0f; *g = ((clear_color >> 5) & 0x1F) / 31.0f; *b = (clear_color & 0x1F) / 31.0f; break; case NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5: *r = ((clear_color >> 11) & 0x1F) / 31.0f; *g = ((clear_color >> 5) & 0x3F) / 63.0f; *b = (clear_color & 0x1F) / 31.0f; break; case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: *r = ((clear_color >> 16) & 0xFF) / 255.0f; *g = ((clear_color >> 8) & 0xFF) / 255.0f; *b = (clear_color & 0xFF) / 255.0f; break; case NV097_SET_SURFACE_FORMAT_COLOR_LE_B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8: /* Xbox D3D doesn't support clearing those */ default: *r = 1.0f; *g = 0.0f; *b = 1.0f; fprintf(stderr, "CLEAR_SURFACE for color_format 0x%x unsupported", pg->surface_shape.color_format); assert(false); break; } /* Handle alpha */ switch(pg->surface_shape.color_format) { /* FIXME: CLEAR_SURFACE seems to work like memset, so maybe we * also have to clear non-alpha bits with alpha value? * As GL doesn't own those pixels we'd have to do this on * our own in xbox memory. */ case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: *a = ((clear_color >> 24) & 0x7F) / 127.0f; assert(false); /* Untested */ break; case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: *a = ((clear_color >> 24) & 0xFF) / 255.0f; break; default: *a = 1.0f; break; } } void pgraph_get_clear_depth_stencil_value(PGRAPHState *pg, float *depth, int *stencil) { uint32_t clear_zstencil = pgraph_reg_r(pg, NV_PGRAPH_ZSTENCILCLEARVALUE); *stencil = 0; *depth = 1.0; switch (pg->surface_shape.zeta_format) { case NV097_SET_SURFACE_FORMAT_ZETA_Z16: { uint16_t z = clear_zstencil & 0xFFFF; /* FIXME: Remove bit for stencil clear? */ if (pg->surface_shape.z_format) { *depth = convert_f16_to_float(z) / f16_max; } else { *depth = z / (float)0xFFFF; } break; } case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: { *stencil = clear_zstencil & 0xFF; uint32_t z = clear_zstencil >> 8; if (pg->surface_shape.z_format) { *depth = convert_f24_to_float(z) / f24_max; } else { *depth = z / (float)0xFFFFFF; } break; } default: fprintf(stderr, "Unknown zeta surface format: 0x%x\n", pg->surface_shape.zeta_format); assert(false); break; } } void pgraph_write_zpass_pixel_cnt_report(NV2AState *d, uint32_t parameter, uint32_t result) { PGRAPHState *pg = &d->pgraph; uint64_t timestamp = 0x0011223344556677; /* FIXME: Update timestamp?! */ uint32_t done = 0; // FIXME: Check hwaddr report_dma_len; uint8_t *report_data = (uint8_t *)nv_dma_map(d, pg->dma_report, &report_dma_len); hwaddr offset = GET_MASK(parameter, NV097_GET_REPORT_OFFSET); assert(offset < report_dma_len); report_data += offset; stq_le_p((uint64_t *)&report_data[0], timestamp); stl_le_p((uint32_t *)&report_data[8], result); stl_le_p((uint32_t *)&report_data[12], done); NV2A_DPRINTF("Report result %d @%" HWADDR_PRIx, result, offset); } void pgraph_process_pending(NV2AState *d) { PGRAPHState *pg = &d->pgraph; pg->renderer->ops.process_pending(d); if (g_config.display.renderer != pg->renderer->type) { qemu_mutex_lock(&d->pgraph.renderer_lock); qemu_mutex_unlock(&d->pfifo.lock); qemu_mutex_lock(&d->pgraph.lock); if (pg->renderer) { qemu_event_reset(&pg->flush_complete); pg->flush_pending = true; qemu_mutex_lock(&d->pfifo.lock); qemu_mutex_unlock(&d->pgraph.lock); if (pg->renderer->ops.process_pending) { pg->renderer->ops.process_pending(d); } qemu_mutex_unlock(&d->pfifo.lock); qemu_mutex_lock(&d->pgraph.lock); while (pg->framebuffer_in_use) { qemu_cond_wait(&d->pgraph.framebuffer_released, &d->pgraph.renderer_lock); } if (pg->renderer->ops.finalize) { pg->renderer->ops.finalize(d); } } init_renderer(pg); qemu_mutex_unlock(&d->pgraph.renderer_lock); qemu_mutex_unlock(&d->pgraph.lock); qemu_mutex_lock(&d->pfifo.lock); } } void pgraph_process_pending_reports(NV2AState *d) { PGRAPHState *pg = &d->pgraph; pg->renderer->ops.process_pending_reports(d); } void pgraph_pre_savevm_trigger(NV2AState *d) { PGRAPHState *pg = &d->pgraph; pg->renderer->ops.pre_savevm_trigger(d); } void pgraph_pre_savevm_wait(NV2AState *d) { PGRAPHState *pg = &d->pgraph; pg->renderer->ops.pre_savevm_wait(d); } void pgraph_pre_shutdown_trigger(NV2AState *d) { PGRAPHState *pg = &d->pgraph; pg->renderer->ops.pre_shutdown_trigger(d); } void pgraph_pre_shutdown_wait(NV2AState *d) { PGRAPHState *pg = &d->pgraph; pg->renderer->ops.pre_shutdown_wait(d); }