xemu/hw/xbox/nv2a/pgraph/gl/draw.c

531 lines
21 KiB
C

/*
* Geforce NV2A PGRAPH OpenGL Renderer
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "qemu/fast-hash.h"
#include "hw/xbox/nv2a/nv2a_int.h"
#include "debug.h"
#include "renderer.h"
void pgraph_gl_clear_surface(NV2AState *d, uint32_t parameter)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
NV2A_DPRINTF("---------PRE CLEAR ------\n");
pg->clearing = true;
GLbitfield gl_mask = 0;
bool write_color = (parameter & NV097_CLEAR_SURFACE_COLOR);
bool write_zeta =
(parameter & (NV097_CLEAR_SURFACE_Z | NV097_CLEAR_SURFACE_STENCIL));
if (write_zeta) {
GLint gl_clear_stencil;
GLfloat gl_clear_depth;
pgraph_get_clear_depth_stencil_value(pg, &gl_clear_depth,
&gl_clear_stencil);
if (parameter & NV097_CLEAR_SURFACE_Z) {
gl_mask |= GL_DEPTH_BUFFER_BIT;
glDepthMask(GL_TRUE);
glClearDepth(gl_clear_depth);
}
if (parameter & NV097_CLEAR_SURFACE_STENCIL) {
gl_mask |= GL_STENCIL_BUFFER_BIT;
glStencilMask(0xff);
glClearStencil(gl_clear_stencil);
}
}
if (write_color) {
gl_mask |= GL_COLOR_BUFFER_BIT;
glColorMask((parameter & NV097_CLEAR_SURFACE_R)
? GL_TRUE : GL_FALSE,
(parameter & NV097_CLEAR_SURFACE_G)
? GL_TRUE : GL_FALSE,
(parameter & NV097_CLEAR_SURFACE_B)
? GL_TRUE : GL_FALSE,
(parameter & NV097_CLEAR_SURFACE_A)
? GL_TRUE : GL_FALSE);
GLfloat rgba[4];
pgraph_get_clear_color(pg, rgba);
glClearColor(rgba[0], rgba[1], rgba[2], rgba[3]);
}
pgraph_gl_surface_update(d, true, write_color, write_zeta);
/* FIXME: Needs confirmation */
unsigned int xmin =
GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CLEARRECTX), NV_PGRAPH_CLEARRECTX_XMIN);
unsigned int xmax =
GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CLEARRECTX), NV_PGRAPH_CLEARRECTX_XMAX);
unsigned int ymin =
GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CLEARRECTY), NV_PGRAPH_CLEARRECTY_YMIN);
unsigned int ymax =
GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CLEARRECTY), NV_PGRAPH_CLEARRECTY_YMAX);
NV2A_DPRINTF(
"------------------CLEAR 0x%x %d,%d - %d,%d %x---------------\n",
parameter, xmin, ymin, xmax, ymax,
d->pgraph.regs_[NV_PGRAPH_COLORCLEARVALUE]);
unsigned int scissor_width = xmax - xmin + 1,
scissor_height = ymax - ymin + 1;
pgraph_apply_anti_aliasing_factor(pg, &xmin, &ymin);
pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height);
ymin = pg->surface_binding_dim.height - (ymin + scissor_height);
NV2A_DPRINTF("Translated clear rect to %d,%d - %d,%d\n", xmin, ymin,
xmin + scissor_width - 1, ymin + scissor_height - 1);
bool full_clear = !xmin && !ymin &&
scissor_width >= pg->surface_binding_dim.width &&
scissor_height >= pg->surface_binding_dim.height;
pgraph_apply_scaling_factor(pg, &xmin, &ymin);
pgraph_apply_scaling_factor(pg, &scissor_width, &scissor_height);
/* FIXME: Respect window clip?!?! */
glEnable(GL_SCISSOR_TEST);
glScissor(xmin, ymin, scissor_width, scissor_height);
/* Dither */
/* FIXME: Maybe also disable it here? + GL implementation dependent */
if (pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) & NV_PGRAPH_CONTROL_0_DITHERENABLE) {
glEnable(GL_DITHER);
} else {
glDisable(GL_DITHER);
}
glClear(gl_mask);
glDisable(GL_SCISSOR_TEST);
pgraph_gl_set_surface_dirty(pg, write_color, write_zeta);
if (r->color_binding) {
r->color_binding->cleared = full_clear && write_color;
}
if (r->zeta_binding) {
r->zeta_binding->cleared = full_clear && write_zeta;
}
pg->clearing = false;
}
void pgraph_gl_draw_begin(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", pg->primitive_mode);
uint32_t control_0 = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0);
bool mask_alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE;
bool mask_red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE;
bool mask_green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE;
bool mask_blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE;
bool color_write = mask_alpha || mask_red || mask_green || mask_blue;
bool depth_test = control_0 & NV_PGRAPH_CONTROL_0_ZENABLE;
bool stencil_test =
pgraph_reg_r(pg, NV_PGRAPH_CONTROL_1) & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE;
bool is_nop_draw = !(color_write || depth_test || stencil_test);
pgraph_gl_surface_update(d, true, true, depth_test || stencil_test);
if (is_nop_draw) {
return;
}
assert(r->color_binding || r->zeta_binding);
pgraph_gl_bind_textures(d);
pgraph_gl_bind_shaders(pg);
glColorMask(mask_red, mask_green, mask_blue, mask_alpha);
glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE));
glStencilMask(GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_1),
NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE));
if (pgraph_reg_r(pg, NV_PGRAPH_BLEND) & NV_PGRAPH_BLEND_EN) {
glEnable(GL_BLEND);
uint32_t sfactor = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_BLEND),
NV_PGRAPH_BLEND_SFACTOR);
uint32_t dfactor = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_BLEND),
NV_PGRAPH_BLEND_DFACTOR);
assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_gl_map));
assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_gl_map));
glBlendFunc(pgraph_blend_factor_gl_map[sfactor],
pgraph_blend_factor_gl_map[dfactor]);
uint32_t equation = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_BLEND),
NV_PGRAPH_BLEND_EQN);
assert(equation < ARRAY_SIZE(pgraph_blend_equation_gl_map));
glBlendEquation(pgraph_blend_equation_gl_map[equation]);
uint32_t blend_color = pgraph_reg_r(pg, NV_PGRAPH_BLENDCOLOR);
float gl_blend_color[4];
pgraph_argb_pack32_to_rgba_float(blend_color, gl_blend_color);
glBlendColor(gl_blend_color[0], gl_blend_color[1], gl_blend_color[2],
gl_blend_color[3]);
} else {
glDisable(GL_BLEND);
}
/* Face culling */
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER)
& NV_PGRAPH_SETUPRASTER_CULLENABLE) {
uint32_t cull_face = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER),
NV_PGRAPH_SETUPRASTER_CULLCTRL);
assert(cull_face < ARRAY_SIZE(pgraph_cull_face_gl_map));
glCullFace(pgraph_cull_face_gl_map[cull_face]);
glEnable(GL_CULL_FACE);
} else {
glDisable(GL_CULL_FACE);
}
/* Clipping */
glEnable(GL_CLIP_DISTANCE0);
glEnable(GL_CLIP_DISTANCE1);
/* Front-face select */
glFrontFace(pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER)
& NV_PGRAPH_SETUPRASTER_FRONTFACE
? GL_CCW : GL_CW);
/* Polygon offset */
/* FIXME: GL implementation-specific, maybe do this in VS? */
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) {
glEnable(GL_POLYGON_OFFSET_FILL);
} else {
glDisable(GL_POLYGON_OFFSET_FILL);
}
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) {
glEnable(GL_POLYGON_OFFSET_LINE);
} else {
glDisable(GL_POLYGON_OFFSET_LINE);
}
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) {
glEnable(GL_POLYGON_OFFSET_POINT);
} else {
glDisable(GL_POLYGON_OFFSET_POINT);
}
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
(NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
uint32_t zfactor_u32 = pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETFACTOR);
GLfloat zfactor = *(float*)&zfactor_u32;
uint32_t zbias_u32 = pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETBIAS);
GLfloat zbias = *(float*)&zbias_u32;
glPolygonOffset(zfactor, zbias);
}
/* Depth testing */
if (depth_test) {
glEnable(GL_DEPTH_TEST);
uint32_t depth_func = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0),
NV_PGRAPH_CONTROL_0_ZFUNC);
assert(depth_func < ARRAY_SIZE(pgraph_depth_func_gl_map));
glDepthFunc(pgraph_depth_func_gl_map[depth_func]);
} else {
glDisable(GL_DEPTH_TEST);
}
if (GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_ZCOMPRESSOCCLUDE),
NV_PGRAPH_ZCOMPRESSOCCLUDE_ZCLAMP_EN) ==
NV_PGRAPH_ZCOMPRESSOCCLUDE_ZCLAMP_EN_CLAMP) {
glEnable(GL_DEPTH_CLAMP);
} else {
glDisable(GL_DEPTH_CLAMP);
}
if (GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3),
NV_PGRAPH_CONTROL_3_SHADEMODE) ==
NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT) {
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
}
if (stencil_test) {
glEnable(GL_STENCIL_TEST);
uint32_t stencil_func = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_1),
NV_PGRAPH_CONTROL_1_STENCIL_FUNC);
uint32_t stencil_ref = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_1),
NV_PGRAPH_CONTROL_1_STENCIL_REF);
uint32_t func_mask = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_1),
NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ);
uint32_t op_fail = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_2),
NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL);
uint32_t op_zfail = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_2),
NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL);
uint32_t op_zpass = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_2),
NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS);
assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_gl_map));
assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_gl_map));
assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_gl_map));
assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_gl_map));
glStencilFunc(
pgraph_stencil_func_gl_map[stencil_func],
stencil_ref,
func_mask);
glStencilOp(
pgraph_stencil_op_gl_map[op_fail],
pgraph_stencil_op_gl_map[op_zfail],
pgraph_stencil_op_gl_map[op_zpass]);
} else {
glDisable(GL_STENCIL_TEST);
}
/* Dither */
/* FIXME: GL implementation dependent */
if (pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) &
NV_PGRAPH_CONTROL_0_DITHERENABLE) {
glEnable(GL_DITHER);
} else {
glDisable(GL_DITHER);
}
glEnable(GL_PROGRAM_POINT_SIZE);
bool anti_aliasing = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_ANTIALIASING), NV_PGRAPH_ANTIALIASING_ENABLE);
/* Edge Antialiasing */
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));
} else {
glDisable(GL_LINE_SMOOTH);
glLineWidth(MIN(r->supported_aliased_line_width_range[1], pg->surface_scale_factor));
}
if (!anti_aliasing && pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
NV_PGRAPH_SETUPRASTER_POLYSMOOTHENABLE) {
glEnable(GL_POLYGON_SMOOTH);
} else {
glDisable(GL_POLYGON_SMOOTH);
}
unsigned int vp_width = pg->surface_binding_dim.width,
vp_height = pg->surface_binding_dim.height;
pgraph_apply_scaling_factor(pg, &vp_width, &vp_height);
glViewport(0, 0, vp_width, vp_height);
/* Surface clip */
/* FIXME: Consider moving to PSH w/ window clip */
unsigned int xmin = pg->surface_shape.clip_x - pg->surface_binding_dim.clip_x,
ymin = pg->surface_shape.clip_y - pg->surface_binding_dim.clip_y;
unsigned int xmax = xmin + pg->surface_shape.clip_width - 1,
ymax = ymin + pg->surface_shape.clip_height - 1;
unsigned int scissor_width = xmax - xmin + 1,
scissor_height = ymax - ymin + 1;
pgraph_apply_anti_aliasing_factor(pg, &xmin, &ymin);
pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height);
ymin = pg->surface_binding_dim.height - (ymin + scissor_height);
pgraph_apply_scaling_factor(pg, &xmin, &ymin);
pgraph_apply_scaling_factor(pg, &scissor_width, &scissor_height);
glEnable(GL_SCISSOR_TEST);
glScissor(xmin, ymin, scissor_width, scissor_height);
/* Visibility testing */
if (pg->zpass_pixel_count_enable) {
r->gl_zpass_pixel_count_query_count++;
r->gl_zpass_pixel_count_queries = (GLuint*)g_realloc(
r->gl_zpass_pixel_count_queries,
sizeof(GLuint) * r->gl_zpass_pixel_count_query_count);
GLuint gl_query;
glGenQueries(1, &gl_query);
r->gl_zpass_pixel_count_queries[
r->gl_zpass_pixel_count_query_count - 1] = gl_query;
glBeginQuery(GL_SAMPLES_PASSED, gl_query);
}
}
void pgraph_gl_draw_end(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
uint32_t control_0 = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0);
bool mask_alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE;
bool mask_red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE;
bool mask_green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE;
bool mask_blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE;
bool color_write = mask_alpha || mask_red || mask_green || mask_blue;
bool depth_test = control_0 & NV_PGRAPH_CONTROL_0_ZENABLE;
bool stencil_test =
pgraph_reg_r(pg, NV_PGRAPH_CONTROL_1) & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE;
bool is_nop_draw = !(color_write || depth_test || stencil_test);
if (is_nop_draw) {
// FIXME: Check PGRAPH register 0x880.
// HW uses bit 11 in 0x880 to enable or disable a color/zeta limit
// check that will raise an exception in the case that a draw should
// modify the color and/or zeta buffer but the target(s) are masked
// off. This check only seems to trigger during the fragment
// processing, it is legal to attempt a draw that is entirely
// clipped regardless of 0x880. See xemu#635 for context.
return;
}
pgraph_gl_flush_draw(d);
/* End of visibility testing */
if (pg->zpass_pixel_count_enable) {
nv2a_profile_inc_counter(NV2A_PROF_QUERY);
glEndQuery(GL_SAMPLES_PASSED);
}
pg->draw_time++;
if (r->color_binding && pgraph_color_write_enabled(pg)) {
r->color_binding->draw_time = pg->draw_time;
}
if (r->zeta_binding && pgraph_zeta_write_enabled(pg)) {
r->zeta_binding->draw_time = pg->draw_time;
}
pgraph_gl_set_surface_dirty(pg, color_write, depth_test || stencil_test);
NV2A_GL_DGROUP_END();
}
void pgraph_gl_flush_draw(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
if (!(r->color_binding || r->zeta_binding)) {
return;
}
assert(r->shader_binding);
if (pg->draw_arrays_length) {
NV2A_GL_DPRINTF(false, "Draw Arrays");
nv2a_profile_inc_counter(NV2A_PROF_DRAW_ARRAYS);
assert(pg->inline_elements_length == 0);
assert(pg->inline_buffer_length == 0);
assert(pg->inline_array_length == 0);
pgraph_gl_bind_vertex_attributes(d, pg->draw_arrays_min_start,
pg->draw_arrays_max_count - 1,
false, 0,
pg->draw_arrays_max_count - 1);
glMultiDrawArrays(r->shader_binding->gl_primitive_mode,
pg->draw_arrays_start,
pg->draw_arrays_count,
pg->draw_arrays_length);
} else if (pg->inline_elements_length) {
NV2A_GL_DPRINTF(false, "Inline Elements");
nv2a_profile_inc_counter(NV2A_PROF_INLINE_ELEMENTS);
assert(pg->inline_buffer_length == 0);
assert(pg->inline_array_length == 0);
uint32_t min_element = (uint32_t)-1;
uint32_t max_element = 0;
for (int i=0; i < pg->inline_elements_length; i++) {
max_element = MAX(pg->inline_elements[i], max_element);
min_element = MIN(pg->inline_elements[i], min_element);
}
pgraph_gl_bind_vertex_attributes(
d, min_element, max_element, false, 0,
pg->inline_elements[pg->inline_elements_length - 1]);
VertexKey k;
memset(&k, 0, sizeof(VertexKey));
k.count = pg->inline_elements_length;
k.gl_type = GL_UNSIGNED_INT;
k.gl_normalize = GL_FALSE;
k.stride = sizeof(uint32_t);
uint64_t h = fast_hash((uint8_t*)pg->inline_elements,
pg->inline_elements_length * 4);
LruNode *node = lru_lookup(&r->element_cache, h, &k);
VertexLruNode *found = container_of(node, VertexLruNode, node);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, found->gl_buffer);
if (!found->initialized) {
nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_4);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
pg->inline_elements_length * 4,
pg->inline_elements, GL_STATIC_DRAW);
found->initialized = true;
} else {
nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_4_NOTDIRTY);
}
glDrawElements(r->shader_binding->gl_primitive_mode,
pg->inline_elements_length, GL_UNSIGNED_INT,
(void *)0);
} else if (pg->inline_buffer_length) {
NV2A_GL_DPRINTF(false, "Inline Buffer");
nv2a_profile_inc_counter(NV2A_PROF_INLINE_BUFFERS);
assert(pg->inline_array_length == 0);
if (pg->compressed_attrs) {
pg->compressed_attrs = 0;
pgraph_gl_bind_shaders(pg);
}
for (int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
VertexAttribute *attr = &pg->vertex_attributes[i];
if (attr->inline_buffer_populated) {
nv2a_profile_inc_counter(NV2A_PROF_GEOM_BUFFER_UPDATE_3);
glBindBuffer(GL_ARRAY_BUFFER, r->gl_inline_buffer[i]);
glBufferData(GL_ARRAY_BUFFER,
pg->inline_buffer_length * sizeof(float) * 4,
attr->inline_buffer, GL_STREAM_DRAW);
glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(i);
attr->inline_buffer_populated = false;
memcpy(attr->inline_value,
attr->inline_buffer + (pg->inline_buffer_length - 1) * 4,
sizeof(attr->inline_value));
} else {
glDisableVertexAttribArray(i);
glVertexAttrib4fv(i, attr->inline_value);
}
}
glDrawArrays(r->shader_binding->gl_primitive_mode,
0, pg->inline_buffer_length);
} else if (pg->inline_array_length) {
NV2A_GL_DPRINTF(false, "Inline Array");
nv2a_profile_inc_counter(NV2A_PROF_INLINE_ARRAYS);
unsigned int index_count = pgraph_gl_bind_inline_array(d);
glDrawArrays(r->shader_binding->gl_primitive_mode,
0, index_count);
} else {
NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END");
NV2A_UNCONFIRMED("EMPTY NV097_SET_BEGIN_END");
}
}