diff --git a/hw/xbox/nv2a/pgraph/gl/draw.c b/hw/xbox/nv2a/pgraph/gl/draw.c index c802ddaa3c..6347a10c44 100644 --- a/hw/xbox/nv2a/pgraph/gl/draw.c +++ b/hw/xbox/nv2a/pgraph/gl/draw.c @@ -27,6 +27,21 @@ #ifdef __ANDROID__ #include + +static void android_log_gl_errors(const char *ctx) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at %s", err, ctx); + } +} +#else +static inline void android_log_gl_errors(const char *ctx) +{ + (void)ctx; +} #endif void pgraph_gl_clear_surface(NV2AState *d, uint32_t parameter) @@ -351,6 +366,8 @@ void pgraph_gl_draw_begin(NV2AState *d) glBeginQuery(GL_SAMPLES_PASSED, gl_query); } #endif + + android_log_gl_errors("pgraph_gl_draw_begin"); } void pgraph_gl_draw_end(NV2AState *d) @@ -456,6 +473,7 @@ void pgraph_gl_flush_draw(NV2AState *d) pg->draw_arrays_start, pg->draw_arrays_count, pg->draw_arrays_length); } + android_log_gl_errors("pgraph_gl_flush_draw: draw_arrays"); } else if (pg->inline_elements_length) { NV2A_GL_DPRINTF(false, "Inline Elements"); nv2a_profile_inc_counter(NV2A_PROF_INLINE_ELEMENTS); @@ -517,6 +535,7 @@ void pgraph_gl_flush_draw(NV2AState *d) pg->inline_elements_length, GL_UNSIGNED_INT, (void *)0); } + android_log_gl_errors("pgraph_gl_flush_draw: inline_elements"); } else if (pg->inline_buffer_length) { NV2A_GL_DPRINTF(false, "Inline Buffer"); nv2a_profile_inc_counter(NV2A_PROF_INLINE_BUFFERS); @@ -563,6 +582,7 @@ void pgraph_gl_flush_draw(NV2AState *d) glDrawArrays(r->shader_binding->gl_primitive_mode, 0, pg->inline_buffer_length); } + android_log_gl_errors("pgraph_gl_flush_draw: inline_buffer"); } else if (pg->inline_array_length) { NV2A_GL_DPRINTF(false, "Inline Array"); nv2a_profile_inc_counter(NV2A_PROF_INLINE_ARRAYS); @@ -584,6 +604,7 @@ void pgraph_gl_flush_draw(NV2AState *d) glDrawArrays(r->shader_binding->gl_primitive_mode, 0, index_count); } + android_log_gl_errors("pgraph_gl_flush_draw: inline_array"); } else { NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END"); NV2A_UNCONFIRMED("EMPTY NV097_SET_BEGIN_END"); diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h index a800181365..695c8cd0ad 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.h +++ b/hw/xbox/nv2a/pgraph/gl/renderer.h @@ -217,6 +217,11 @@ typedef struct PGRAPHGLState { struct s2t_rndr { GLuint fbo, vao, vbo, prog; GLuint tex_loc, surface_size_loc; +#ifdef __ANDROID__ + GLuint depth_prog; + GLuint depth_tex_loc; + GLint depth_scale_loc; +#endif } s2t_rndr; struct disp_rndr { diff --git a/hw/xbox/nv2a/pgraph/gl/shaders.c b/hw/xbox/nv2a/pgraph/gl/shaders.c index 015c82437b..8ac6f43567 100644 --- a/hw/xbox/nv2a/pgraph/gl/shaders.c +++ b/hw/xbox/nv2a/pgraph/gl/shaders.c @@ -45,6 +45,45 @@ static GLenum get_gl_primitive_mode(enum ShaderPrimitiveMode primitive_mode) } } +#ifdef __ANDROID__ +static void android_log_shader_stage_errors(const char *ctx) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at %s", err, ctx); + } +} + +static void android_log_apply_uniform_entry_errors(const char *uniform_set) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X before apply_uniform_updates:%s", + err, uniform_set); + } +} + +static void android_log_uniform_update_errors(const char *uniform_set, + const UniformInfo *info, + int loc) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print( + ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at apply_uniform_updates:%s.%s type=%s count=%zu " + "loc=%d", + err, uniform_set, info->name, + uniform_element_type_to_str[info->type], info->count, loc); + } +} +#endif + static void log_shader_source_with_line_numbers(const char *name, const char *code) { @@ -734,9 +773,14 @@ void pgraph_gl_shader_cache_to_disk(ShaderBinding *binding) qemu_thread_create(binding->save_thread, name, shader_write_to_disk, binding, QEMU_THREAD_JOINABLE); } -static void apply_uniform_updates(const UniformInfo *info, int *locs, +static void apply_uniform_updates(const char *uniform_set, + const UniformInfo *info, int *locs, void *values, size_t count) { +#ifdef __ANDROID__ + android_log_apply_uniform_entry_errors(uniform_set); +#endif + for (int i = 0; i < count; i++) { if (locs[i] == -1) { continue; @@ -775,13 +819,13 @@ static void apply_uniform_updates(const UniformInfo *info, int *locs, default: g_assert_not_reached(); } - } #ifdef __ANDROID__ - while (glGetError() != GL_NO_ERROR) { - /* Ignore uniform update GL errors on Android. */ + android_log_uniform_update_errors(uniform_set, &info[i], locs[i]); +#endif } -#else + +#ifndef __ANDROID__ assert(glGetError() == GL_NO_ERROR); #endif } @@ -795,7 +839,7 @@ static void update_shader_uniforms(PGRAPHState *pg, ShaderBinding *binding) VshUniformValues vsh_values; pgraph_glsl_set_vsh_uniform_values(pg, &binding->state.vsh, binding->uniform_locs.vsh, &vsh_values); - apply_uniform_updates(VshUniformInfo, binding->uniform_locs.vsh, + apply_uniform_updates("vsh", VshUniformInfo, binding->uniform_locs.vsh, &vsh_values, VshUniform__COUNT); PshUniformValues psh_values; @@ -807,7 +851,7 @@ static void update_shader_uniforms(PGRAPHState *pg, ShaderBinding *binding) psh_values.texScale[i] = scale; } } - apply_uniform_updates(PshUniformInfo, binding->uniform_locs.psh, + apply_uniform_updates("psh", PshUniformInfo, binding->uniform_locs.psh, &psh_values, PshUniform__COUNT); } @@ -853,6 +897,10 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg) if (binding_changed) { nv2a_profile_inc_counter(NV2A_PROF_SHADER_BIND); glUseProgram(r->shader_binding->gl_program); +#ifdef __ANDROID__ + android_log_shader_stage_errors( + "pgraph_gl_bind_shaders: binding_changed"); +#endif } NV2A_GL_DGROUP_END(); @@ -860,6 +908,10 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg) update_uniforms: assert(r->shader_binding); assert(r->shader_binding->initialized); + glUseProgram(r->shader_binding->gl_program); +#ifdef __ANDROID__ + android_log_shader_stage_errors("pgraph_gl_bind_shaders: update_uniforms"); +#endif update_shader_uniforms(pg, r->shader_binding); } diff --git a/hw/xbox/nv2a/pgraph/gl/surface.c b/hw/xbox/nv2a/pgraph/gl/surface.c index e99c43a45c..2333d85c32 100644 --- a/hw/xbox/nv2a/pgraph/gl/surface.c +++ b/hw/xbox/nv2a/pgraph/gl/surface.c @@ -31,6 +31,57 @@ #endif #ifdef __ANDROID__ +static bool android_log_and_drain_gl_errors(const char *ctx) +{ + GLenum err; + bool had_error = false; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at %s", err, ctx); + had_error = true; + } + + return had_error; +} + +static bool android_log_surface_download_errors(const char *ctx, + const SurfaceBinding *surface) +{ + bool had_error = android_log_and_drain_gl_errors(ctx); + + if (!had_error || !surface) { + return had_error; + } + + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + " surface download: kind=%s attachment=0x%X format=0x%X " + "type=0x%X bpp=%u size=%ux%u pitch=%u addr=0x%llX", + surface->color ? "color" : "zeta", + surface->fmt.gl_attachment, + surface->fmt.gl_format, + surface->fmt.gl_type, + surface->fmt.bytes_per_pixel, + surface->width, + surface->height, + surface->pitch, + (unsigned long long)surface->vram_addr); + + return had_error; +} + +static bool android_drain_gl_errors_silent(void) +{ + GLenum err; + bool had_error = false; + + while ((err = glGetError()) != GL_NO_ERROR) { + had_error = true; + } + + return had_error; +} + static void android_sanitize_surface_format(PGRAPHGLState *r, SurfaceFormatInfo *fmt) { @@ -479,11 +530,37 @@ static void init_render_to_texture(PGRAPHState *pg) " vec2 texCoord = gl_FragCoord.xy / texSize;\n" " out_Color.rgba = texture(tex, texCoord);\n" "}\n"; +#ifdef __ANDROID__ + const char *depth_fs = + "#version 300 es\n" + "precision highp float;\n" + "precision highp int;\n" + "precision highp sampler2D;\n" + "uniform sampler2D tex;\n" + "uniform float depth_scale;\n" + "layout(location = 0) out vec4 out_Color;\n" + "void main()\n" + "{\n" + " float depth = texelFetch(tex, ivec2(gl_FragCoord.xy), 0).r;\n" + " uint packed = uint(clamp(depth * depth_scale + 0.5, 0.0, depth_scale));\n" + " out_Color = vec4(float(packed & 0xFFu) / 255.0,\n" + " float((packed >> 8) & 0xFFu) / 255.0,\n" + " float((packed >> 16) & 0xFFu) / 255.0,\n" + " 1.0);\n" + "}\n"; +#endif r->s2t_rndr.prog = pgraph_gl_compile_shader(vs, fs); r->s2t_rndr.tex_loc = glGetUniformLocation(r->s2t_rndr.prog, "tex"); r->s2t_rndr.surface_size_loc = glGetUniformLocation(r->s2t_rndr.prog, "surface_size"); +#ifdef __ANDROID__ + r->s2t_rndr.depth_prog = pgraph_gl_compile_shader(vs, depth_fs); + r->s2t_rndr.depth_tex_loc = + glGetUniformLocation(r->s2t_rndr.depth_prog, "tex"); + r->s2t_rndr.depth_scale_loc = + glGetUniformLocation(r->s2t_rndr.depth_prog, "depth_scale"); +#endif glGenVertexArrays(1, &r->s2t_rndr.vao); glBindVertexArray(r->s2t_rndr.vao); @@ -499,6 +576,10 @@ static void finalize_render_to_texture(PGRAPHState *pg) glDeleteProgram(r->s2t_rndr.prog); r->s2t_rndr.prog = 0; +#ifdef __ANDROID__ + glDeleteProgram(r->s2t_rndr.depth_prog); + r->s2t_rndr.depth_prog = 0; +#endif glDeleteVertexArrays(1, &r->s2t_rndr.vao); r->s2t_rndr.vao = 0; @@ -610,7 +691,7 @@ static bool render_surface_to(NV2AState *d, SurfaceBinding *surface, #ifndef __ANDROID__ assert(glGetError() == GL_NO_ERROR); #else - if (glGetError() != GL_NO_ERROR) { + if (android_log_and_drain_gl_errors("render_surface_to: fbo setup")) { glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer); return false; } @@ -630,10 +711,12 @@ static bool render_surface_to(NV2AState *d, SurfaceBinding *surface, glBindVertexArray(r->s2t_rndr.vao); glBindBuffer(GL_ARRAY_BUFFER, r->s2t_rndr.vbo); glUseProgram(r->s2t_rndr.prog); - glProgramUniform1i(r->s2t_rndr.prog, r->s2t_rndr.tex_loc, - texture_unit); - glProgramUniform2f(r->s2t_rndr.prog, - r->s2t_rndr.surface_size_loc, width, height); + if (r->s2t_rndr.tex_loc >= 0) { + glUniform1i(r->s2t_rndr.tex_loc, texture_unit); + } + if (r->s2t_rndr.surface_size_loc >= 0) { + glUniform2f(r->s2t_rndr.surface_size_loc, width, height); + } glViewport(0, 0, width, height); glColorMask(true, true, true, true); @@ -650,6 +733,18 @@ static bool render_surface_to(NV2AState *d, SurfaceBinding *surface, glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 3); +#ifdef __ANDROID__ + if (android_log_and_drain_gl_errors("render_surface_to: draw")) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_target, + 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer); + glBindVertexArray(r->gl_vertex_array); + glBindTexture(gl_target, gl_texture); + glUseProgram(r->shader_binding ? r->shader_binding->gl_program : 0); + return false; + } +#endif + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_target, 0, 0); glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer); @@ -1085,6 +1180,16 @@ static void bind_current_surface(NV2AState *d) PGRAPHState *pg = &d->pgraph; PGRAPHGLState *r = pg->gl_renderer_state; + /* Clear any temporary download/render attachments before restoring + * the active render targets. + */ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + if (r->color_binding) { glFramebufferTexture2D(GL_FRAMEBUFFER, r->color_binding->fmt.gl_attachment, GL_TEXTURE_2D, r->color_binding->gl_buffer, 0); @@ -1170,12 +1275,351 @@ static void surface_copy_shrink_row(uint8_t *out, uint8_t *in, } } +#ifdef __ANDROID__ +static bool android_surface_download_depth16_to_guest(NV2AState *d, + SurfaceBinding *surface, + bool swizzle, bool flip, + bool downscale, + uint8_t *pixels) +{ + PGRAPHState *pg = &d->pgraph; + PGRAPHGLState *r = pg->gl_renderer_state; + const unsigned int factor = downscale ? pg->surface_scale_factor : 1; + const unsigned int read_width = surface->width * factor; + const unsigned int read_height = surface->height * factor; + const unsigned int rgba_stride = read_width * 4; + GLuint pack_texture = 0; + uint8_t *rgba_pixels = NULL; + uint8_t *rgba_linear = NULL; + uint8_t *linear_guest = pixels; + uint8_t *swizzle_buf = NULL; + bool ok = false; + + if (surface->color || surface->fmt.gl_attachment != GL_DEPTH_ATTACHMENT || + surface->fmt.gl_format != GL_DEPTH_COMPONENT || + surface->fmt.gl_type != GL_UNSIGNED_SHORT || + r->s2t_rndr.depth_prog == 0) { + return false; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, surface->gl_buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glGenTextures(1, &pack_texture); + glBindTexture(GL_TEXTURE_2D, pack_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, read_width, read_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glBindFramebuffer(GL_FRAMEBUFFER, r->s2t_rndr.fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + pack_texture, 0); + { + GLenum draw_buffers[1] = { GL_COLOR_ATTACHMENT0 }; + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + goto cleanup; + } + glDrawBuffers(1, draw_buffers); + glReadBuffer(GL_COLOR_ATTACHMENT0); + } + + if (android_log_surface_download_errors( + "surface_download_depth16: fbo setup", surface)) { + goto cleanup; + } + + glBindVertexArray(r->s2t_rndr.vao); + glBindBuffer(GL_ARRAY_BUFFER, r->s2t_rndr.vbo); + glUseProgram(r->s2t_rndr.depth_prog); + if ((GLint)r->s2t_rndr.depth_tex_loc >= 0) { + glUniform1i(r->s2t_rndr.depth_tex_loc, 0); + } + if (r->s2t_rndr.depth_scale_loc >= 0) { + glUniform1f(r->s2t_rndr.depth_scale_loc, 65535.0f); + } + glViewport(0, 0, read_width, read_height); + glColorMask(true, true, true, true); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_STENCIL_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 3); + + if (android_log_surface_download_errors( + "surface_download_depth16: pack draw", surface)) { + goto cleanup; + } + + rgba_pixels = g_malloc(read_height * rgba_stride); + glo_readpixels(GL_RGBA, GL_UNSIGNED_BYTE, 4, rgba_stride, read_width, + read_height, flip, rgba_pixels); + + if (android_log_surface_download_errors( + "surface_download_depth16: pack read", surface)) { + goto cleanup; + } + + rgba_linear = rgba_pixels; + if (downscale) { + rgba_linear = g_malloc(surface->width * surface->height * 4); + for (unsigned int y = 0; y < surface->height; y++) { + surface_copy_shrink_row(rgba_linear + y * surface->width * 4, + rgba_pixels + y * rgba_stride * factor, + surface->width, 4, factor); + } + } + + if (swizzle) { + swizzle_buf = g_malloc(surface->size); + linear_guest = swizzle_buf; + } + + for (unsigned int y = 0; y < surface->height; y++) { + const uint8_t *src_row = rgba_linear + y * surface->width * 4; + uint8_t *dst_row = linear_guest + y * surface->pitch; + + for (unsigned int x = 0; x < surface->width; x++) { + const uint8_t *src = src_row + x * 4; + stw_le_p(dst_row + x * 2, src[0] | (src[1] << 8)); + } + } + + if (swizzle) { + swizzle_rect(swizzle_buf, surface->width, surface->height, pixels, + surface->pitch, surface->fmt.bytes_per_pixel); + } + + ok = true; + +cleanup: + if (swizzle_buf) { + g_free(swizzle_buf); + } + if (rgba_linear && rgba_linear != rgba_pixels) { + g_free(rgba_linear); + } + if (rgba_pixels) { + g_free(rgba_pixels); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + 0, 0); + if (pack_texture) { + glDeleteTextures(1, &pack_texture); + } + glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer); + glBindVertexArray(r->gl_vertex_array); + glUseProgram(r->shader_binding ? r->shader_binding->gl_program : 0); + + return ok; +} + +static bool android_surface_download_z24s8_to_guest(NV2AState *d, + SurfaceBinding *surface, + bool swizzle, bool flip, + bool downscale, + uint8_t *pixels) +{ + PGRAPHState *pg = &d->pgraph; + PGRAPHGLState *r = pg->gl_renderer_state; + const unsigned int scale = pg->surface_scale_factor; + const unsigned int read_width = surface->width * scale; + const unsigned int read_height = surface->height * scale; + const unsigned int output_width = downscale ? surface->width : read_width; + const unsigned int output_height = downscale ? surface->height : read_height; + const unsigned int output_pitch = downscale ? surface->pitch + : surface->pitch * scale; + GLuint pack_texture = 0; + uint8_t *depth_pixels = NULL; + uint8_t *stencil_pixels = NULL; + uint8_t *linear_guest = pixels; + uint8_t *swizzle_buf = NULL; + GLint prev_read_buffer = GL_NONE; + bool ok = false; + static bool warned_stencil_readback_unsupported = false; + + if (surface->color || + surface->fmt.gl_attachment != GL_DEPTH_STENCIL_ATTACHMENT || + surface->fmt.gl_format != GL_DEPTH_STENCIL || + surface->fmt.gl_type != GL_UNSIGNED_INT_24_8) { + return false; + } + + glGetIntegerv(GL_READ_BUFFER, &prev_read_buffer); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, surface->gl_buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glGenTextures(1, &pack_texture); + glBindTexture(GL_TEXTURE_2D, pack_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, read_width, read_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glBindFramebuffer(GL_FRAMEBUFFER, r->s2t_rndr.fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + pack_texture, 0); + { + GLenum draw_buffers[1] = { GL_COLOR_ATTACHMENT0 }; + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + goto cleanup; + } + glDrawBuffers(1, draw_buffers); + glReadBuffer(GL_COLOR_ATTACHMENT0); + } + + if (android_log_surface_download_errors( + "surface_download_z24s8: pack_fbo_setup", surface)) { + goto cleanup; + } + + glBindVertexArray(r->s2t_rndr.vao); + glBindBuffer(GL_ARRAY_BUFFER, r->s2t_rndr.vbo); + glUseProgram(r->s2t_rndr.depth_prog); + if ((GLint)r->s2t_rndr.depth_tex_loc >= 0) { + glUniform1i(r->s2t_rndr.depth_tex_loc, 0); + } + if (r->s2t_rndr.depth_scale_loc >= 0) { + glUniform1f(r->s2t_rndr.depth_scale_loc, 16777215.0f); + } + glViewport(0, 0, read_width, read_height); + glColorMask(true, true, true, true); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_STENCIL_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 3); + + if (android_log_surface_download_errors( + "surface_download_z24s8: pack_draw", surface)) { + goto cleanup; + } + + depth_pixels = g_malloc(read_width * read_height * 4); + glo_readpixels(GL_RGBA, GL_UNSIGNED_BYTE, 4, read_width * 4, read_width, + read_height, flip, depth_pixels); + if (android_log_surface_download_errors( + "surface_download_z24s8: post-read-depth-pack", surface)) { + goto cleanup; + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer); + glBindVertexArray(r->gl_vertex_array); + glUseProgram(r->shader_binding ? r->shader_binding->gl_program : 0); + + glReadBuffer(GL_NONE); + if (android_log_surface_download_errors( + "surface_download_z24s8: pre-read-stencil", surface)) { + goto cleanup; + } + + stencil_pixels = g_malloc0(read_width * read_height); + glo_readpixels(GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 1, read_width, + read_width, read_height, flip, + stencil_pixels); + if (android_drain_gl_errors_silent()) { + if (!warned_stencil_readback_unsupported) { + warned_stencil_readback_unsupported = true; + __android_log_print( + ANDROID_LOG_WARN, "xemu-android", + "surface_download_z24s8: stencil readback unsupported on this " + "GLES driver, substituting zero stencil"); + } + } + + if (swizzle) { + swizzle_buf = g_malloc(output_pitch * output_height); + linear_guest = swizzle_buf; + } + + for (unsigned int y = 0; y < output_height; y++) { + uint8_t *dst_row = linear_guest + y * output_pitch; + const unsigned int src_y = downscale ? y * scale : y; + + for (unsigned int x = 0; x < output_width; x++) { + const unsigned int src_x = downscale ? x * scale : x; + const unsigned int src_idx = src_y * read_width + src_x; + const uint8_t *src = depth_pixels + src_idx * 4; + uint32_t depth24 = src[0] | (src[1] << 8) | (src[2] << 16); + + stl_le_p((uint32_t *)(dst_row + x * 4), + (depth24 << 8) | stencil_pixels[src_idx]); + } + } + + if (swizzle) { + swizzle_rect(swizzle_buf, output_width, output_height, pixels, + output_pitch, surface->fmt.bytes_per_pixel); + } + + ok = true; + +cleanup: + if (swizzle_buf) { + g_free(swizzle_buf); + } + if (stencil_pixels) { + g_free(stencil_pixels); + } + if (depth_pixels) { + g_free(depth_pixels); + } + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + 0, 0); + if (pack_texture) { + glDeleteTextures(1, &pack_texture); + } + glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer); + glBindVertexArray(r->gl_vertex_array); + glUseProgram(r->shader_binding ? r->shader_binding->gl_program : 0); + + glReadBuffer((GLenum)prev_read_buffer); + android_log_surface_download_errors( + "surface_download_z24s8: cleanup_restore_read_buffer", surface); + + return ok; +} +#endif + static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface, bool swizzle, bool flip, bool downscale, uint8_t *pixels) { PGRAPHState *pg = &d->pgraph; bool ok = true; +#ifdef __ANDROID__ + GLint prev_read_buffer = GL_COLOR_ATTACHMENT0; + bool restore_read_buffer = false; +#endif swizzle &= surface->swizzle; downscale &= (pg->surface_scale_factor != 1); @@ -1232,8 +1676,24 @@ static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface, goto cleanup; } +#ifdef __ANDROID__ + if (android_surface_download_depth16_to_guest(d, surface, swizzle, flip, + downscale, pixels)) { + goto cleanup; + } + if (android_surface_download_z24s8_to_guest(d, surface, swizzle, flip, + downscale, pixels)) { + goto cleanup; + } +#endif + /* Read surface into memory */ #ifdef __ANDROID__ + glGetIntegerv(GL_READ_BUFFER, &prev_read_buffer); + glReadBuffer(surface->color ? surface->fmt.gl_attachment : GL_NONE); + restore_read_buffer = true; + android_log_surface_download_errors("surface_download_to_buffer: pre-read", + surface); if (android_surface_uses_rgba8_transfer(surface)) { const unsigned int factor = downscale ? pg->surface_scale_factor : 1; const unsigned int read_width = surface->width * factor; @@ -1245,6 +1705,8 @@ static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface, glo_readpixels(GL_RGBA, GL_UNSIGNED_BYTE, 4, rgba_stride, read_width, read_height, flip, rgba_pixels); + android_log_surface_download_errors( + "surface_download_to_buffer: post-read_rgba8", surface); if (downscale) { rgba_linear = g_malloc(surface->width * surface->height * 4); @@ -1301,6 +1763,8 @@ static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface, pg->surface_scale_factor * surface->pitch, pg->surface_scale_factor * surface->width, pg->surface_scale_factor * surface->height, flip, gl_read_buf); + android_log_surface_download_errors("surface_download_to_buffer: post-read", + surface); /* FIXME: Replace this with a hw accelerated version */ if (downscale) { @@ -1324,9 +1788,24 @@ static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface, /* Re-bind original framebuffer target */ cleanup: - glFramebufferTexture2D(GL_FRAMEBUFFER, surface->fmt.gl_attachment, - GL_TEXTURE_2D, 0, 0); bind_current_surface(d); +#ifdef __ANDROID__ + android_log_surface_download_errors( + "surface_download_to_buffer: cleanup_rebind_surface", surface); +#endif +#ifdef __ANDROID__ + if (restore_read_buffer) { + GLenum read_buffer = (GLenum)prev_read_buffer; + if (read_buffer != GL_NONE && + !d->pgraph.gl_renderer_state->color_binding) { + read_buffer = GL_NONE; + } + glReadBuffer(read_buffer); + android_log_surface_download_errors( + "surface_download_to_buffer: cleanup_restore_read_buffer", + surface); + } +#endif } static void surface_download(NV2AState *d, SurfaceBinding *surface, bool force) diff --git a/hw/xbox/nv2a/pgraph/gl/texture.c b/hw/xbox/nv2a/pgraph/gl/texture.c index 531cb0591b..1be2e13bea 100644 --- a/hw/xbox/nv2a/pgraph/gl/texture.c +++ b/hw/xbox/nv2a/pgraph/gl/texture.c @@ -28,6 +28,43 @@ #include "renderer.h" #ifdef __ANDROID__ +#include + +static void android_log_gl_errors(const char *ctx) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at %s", err, ctx); + } +} + +static void android_log_texture_stage_errors(int unit, const char *stage, + const TextureShape *shape, + GLenum gl_target) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + if (shape) { + __android_log_print( + ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at pgraph_gl_bind_textures[%d]: %s " + "target=0x%X dim=%u fmt=0x%X levels=%u border=%d cubemap=%d", + err, unit, stage, gl_target, shape->dimensionality, + shape->color_format, shape->levels, shape->border, + shape->cubemap); + } else { + __android_log_print( + ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at pgraph_gl_bind_textures[%d]: %s " + "target=0x%X", + err, unit, stage, gl_target); + } + } +} + static uint8_t android_expand_4_to_8(uint8_t value) { return (value << 4) | value; @@ -515,6 +552,9 @@ void pgraph_gl_bind_textures(NV2AState *d) /* FIXME: What happens if texture is disabled but stage is active? */ glActiveTexture(GL_TEXTURE0 + i); +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "after_active_texture", NULL, 0); +#endif if (!enabled) { glBindTexture(GL_TEXTURE_CUBE_MAP, 0); #ifndef __ANDROID__ @@ -522,6 +562,9 @@ void pgraph_gl_bind_textures(NV2AState *d) #endif glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_3D, 0); +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "disabled_unbind", NULL, 0); +#endif continue; } @@ -574,6 +617,11 @@ void pgraph_gl_bind_textures(NV2AState *d) if (reusable) { glBindTexture(r->texture_binding[i]->gl_target, r->texture_binding[i]->gl_texture); +#ifdef __ANDROID__ + android_log_texture_stage_errors( + i, "reuse_bind_existing", &state, + r->texture_binding[i]->gl_target); +#endif apply_texture_parameters(r, r->texture_binding[i], &kelvin_color_format_info_map[state.color_format], @@ -583,6 +631,11 @@ void pgraph_gl_bind_textures(NV2AState *d) state.border, border_color, max_anisotropy); +#ifdef __ANDROID__ + android_log_texture_stage_errors( + i, "reuse_apply_texture_parameters", &state, + r->texture_binding[i]->gl_target); +#endif continue; } } @@ -597,6 +650,10 @@ void pgraph_gl_bind_textures(NV2AState *d) if (surf_to_tex && surface->upload_pending) { pgraph_gl_upload_surface_data(d, surface, false); +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "surface_upload_pending", + &state, GL_TEXTURE_2D); +#endif } } @@ -611,6 +668,10 @@ void pgraph_gl_bind_textures(NV2AState *d) || texture_vram_offset >= surf_vram_end); if (overlapping) { pgraph_gl_surface_download_if_dirty(d, surface); +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "download_overlap", + &state, GL_TEXTURE_2D); +#endif } } } @@ -667,10 +728,18 @@ void pgraph_gl_bind_textures(NV2AState *d) key_out->binding = generate_texture(state, texture_data, palette_data); key_out->binding->data_hash = tex_data_hash; key_out->binding->scale = 1; +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "generate_texture", &state, + key_out->binding->gl_target); +#endif } else { // Saved an upload! Reuse existing texture in graphics memory. glBindTexture(key_out->binding->gl_target, key_out->binding->gl_texture); +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "reuse_cached_binding", &state, + key_out->binding->gl_target); +#endif } key_out->possibly_dirty = false; @@ -684,6 +753,10 @@ void pgraph_gl_bind_textures(NV2AState *d) pgraph_gl_render_surface_to_texture(d, surface, binding, &state, i); binding->draw_time = surface->draw_time; binding->scale = pg->surface_scale_factor; +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "render_surface_to_texture", + &state, binding->gl_target); +#endif } apply_texture_parameters(r, @@ -695,16 +768,29 @@ void pgraph_gl_bind_textures(NV2AState *d) state.border, border_color, max_anisotropy); +#ifdef __ANDROID__ + android_log_texture_stage_errors(i, "apply_texture_parameters", &state, + binding->gl_target); +#endif if (r->texture_binding[i]) { if (r->texture_binding[i]->gl_target != binding->gl_target) { glBindTexture(r->texture_binding[i]->gl_target, 0); +#ifdef __ANDROID__ + android_log_texture_stage_errors( + i, "unbind_old_target", &state, + r->texture_binding[i]->gl_target); +#endif } texture_binding_destroy(r->texture_binding[i]); } r->texture_binding[i] = binding; pg->texture_dirty[i] = false; } + +#ifdef __ANDROID__ + android_log_gl_errors("pgraph_gl_bind_textures"); +#endif NV2A_GL_DGROUP_END(); } @@ -1143,8 +1229,21 @@ static TextureBinding* generate_texture(const TextureShape s, } #endif if (apply_swizzle) { +#ifdef __ANDROID__ + /* GLES exposes per-channel texture swizzles, not the desktop RGBA + * vector pname. */ + glTexParameteri(gl_target, GL_TEXTURE_SWIZZLE_R, + f.gl_swizzle_mask[0]); + glTexParameteri(gl_target, GL_TEXTURE_SWIZZLE_G, + f.gl_swizzle_mask[1]); + glTexParameteri(gl_target, GL_TEXTURE_SWIZZLE_B, + f.gl_swizzle_mask[2]); + glTexParameteri(gl_target, GL_TEXTURE_SWIZZLE_A, + f.gl_swizzle_mask[3]); +#else glTexParameteriv(gl_target, GL_TEXTURE_SWIZZLE_RGBA, (const GLint *)f.gl_swizzle_mask); +#endif } TextureBinding* ret = (TextureBinding *)g_malloc(sizeof(TextureBinding)); diff --git a/hw/xbox/nv2a/pgraph/gl/vertex.c b/hw/xbox/nv2a/pgraph/gl/vertex.c index 91919dc179..deff977485 100644 --- a/hw/xbox/nv2a/pgraph/gl/vertex.c +++ b/hw/xbox/nv2a/pgraph/gl/vertex.c @@ -24,6 +24,25 @@ #include "debug.h" #include "renderer.h" +#ifdef __ANDROID__ +#include + +static void android_log_gl_errors(const char *ctx) +{ + GLenum err; + + while ((err = glGetError()) != GL_NO_ERROR) { + __android_log_print(ANDROID_LOG_WARN, "xemu-android", + "GL error 0x%X at %s", err, ctx); + } +} +#else +static inline void android_log_gl_errors(const char *ctx) +{ + (void)ctx; +} +#endif + static void update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool quick) { @@ -205,6 +224,7 @@ void pgraph_gl_bind_vertex_attributes(NV2AState *d, unsigned int min_element, pgraph_update_inline_value(attr, last_entry); } + android_log_gl_errors("pgraph_gl_bind_vertex_attributes"); NV2A_GL_DGROUP_END(); }