From f90a4cb5f9f1a943b8b05fed611f5c78f7ca63b9 Mon Sep 17 00:00:00 2001
From: Jannik Vogel <email@jannikvogel.de>
Date: Thu, 3 Sep 2015 18:53:33 +0200
Subject: [PATCH 1/2] Respect surface format for color clears

---
 hw/xbox/nv2a.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 60 insertions(+), 4 deletions(-)

diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c
index e2ab916f92..726c4fc149 100644
--- a/hw/xbox/nv2a.c
+++ b/hw/xbox/nv2a.c
@@ -5735,10 +5735,66 @@ static void pgraph_method(NV2AState *d,
                         (parameter & NV097_CLEAR_SURFACE_A)
                              ? GL_TRUE : GL_FALSE);
             uint32_t clear_color = d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE];
-            glClearColor( ((clear_color >> 16) & 0xFF) / 255.0f, /* red */
-                          ((clear_color >> 8) & 0xFF) / 255.0f,  /* green */
-                          (clear_color & 0xFF) / 255.0f,         /* blue */
-                          ((clear_color >> 24) & 0xFF) / 255.0f);/* alpha */
+
+            /* Handle RGB */
+            GLfloat red, green, blue;
+            switch(pg->surface_shape.color_format) {
+            case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5:
+            case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5:
+                red = ((clear_color >> 10) & 0x1F) / 31.0f;
+                green = ((clear_color >> 5) & 0x1F) / 31.0f;
+                blue = (clear_color & 0x1F) / 31.0f;
+                assert(false); /* Untested */
+                break;
+            case NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5:
+                red = ((clear_color >> 11) & 0x1F) / 31.0f;
+                green = ((clear_color >> 5) & 0x3F) / 63.0f;
+                blue = (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:
+                red = ((clear_color >> 16) & 0xFF) / 255.0f;
+                green = ((clear_color >> 8) & 0xFF) / 255.0f;
+                blue = (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:
+                red = 1.0f;
+                green = 0.0f;
+                blue = 1.0f;
+                fprintf(stderr, "CLEAR_SURFACE for color_format 0x%x unsupported",
+                        pg->surface_shape.color_format);
+                assert(false);
+                break;
+            }
+
+            /* Handle alpha */
+            GLfloat 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:
+                alpha = ((clear_color >> 24) & 0x7F) / 127.0f;
+                assert(false); /* Untested */
+                break;
+            case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8:
+                alpha = ((clear_color >> 24) & 0xFF) / 255.0f;
+                break;
+            default:
+                alpha = 1.0f;
+                break;
+            }
+
+            glClearColor(red, green, blue, alpha);
         }
         pgraph_update_surface(d, true, write_color, write_zeta);
 

From e754087e181c305e37bbd24e59c4fe961bc4f68d Mon Sep 17 00:00:00 2001
From: Jannik Vogel <email@jannikvogel.de>
Date: Fri, 4 Sep 2015 01:23:41 +0200
Subject: [PATCH 2/2] Respect surface format for zeta clears

---
 hw/xbox/nv2a.c | 59 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 48 insertions(+), 11 deletions(-)

diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c
index 726c4fc149..4277262b1f 100644
--- a/hw/xbox/nv2a.c
+++ b/hw/xbox/nv2a.c
@@ -2002,6 +2002,22 @@ static GraphicsObject* lookup_graphics_object(PGRAPHState *s,
     return NULL;
 }
 
+/* 16 bit to [0.0, F16_MAX = 511.9375] */
+static float convert_f16_to_float(uint16_t f16) {
+    if (f16 == 0x0000) { return 0.0; }
+    uint32_t i = (f16 << 11) + 0x3C000000;
+    return *(float*)&i;
+}
+
+/* 24 bit to [0.0, F24_MAX] */
+static float convert_f24_to_float(uint32_t f24) {
+    assert(!(f24 >> 24));
+    f24 &= 0xFFFFFF;
+    if (f24 == 0x000000) { return 0.0; }
+    uint32_t i = f24 << 7;
+    return *(float*)&i;
+}
+
 static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size,
                                         bool f)
 {
@@ -5700,18 +5716,39 @@ static void pgraph_method(NV2AState *d,
             uint32_t clear_zstencil =
                 d->pgraph.regs[NV_PGRAPH_ZSTENCILCLEARVALUE];
             GLint gl_clear_stencil;
-            GLdouble gl_clear_depth;
+            GLfloat gl_clear_depth;
+
+            /* FIXME: Put these in some lookup table */
+            const float f16_max = 511.9375f;
+            /* FIXME: 7 bits of mantissa unused. maybe use full buffer? */
+            const float f24_max = 3.4027977E38;
+
             switch(pg->surface_shape.zeta_format) {
-                case NV097_SET_SURFACE_FORMAT_ZETA_Z16:
-                    /* FIXME: Remove bit for stencil clear? */
-                    gl_clear_depth = (clear_zstencil & 0xFFFF) / (double)0xFFFF;
-                    break;
-                case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8:
-                    gl_clear_stencil = clear_zstencil & 0xFF;
-                    gl_clear_depth = (clear_zstencil >> 8) / (double)0xFFFFFF;
-                    break;
-                default:
-                    assert(0);
+            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) {
+                    gl_clear_depth = convert_f16_to_float(z) / f16_max;
+                    assert(false); /* FIXME: Untested */
+                } else {
+                    gl_clear_depth = z / (float)0xFFFF;
+                }
+                break;
+            }
+            case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: {
+                gl_clear_stencil = clear_zstencil & 0xFF;
+                uint32_t z = clear_zstencil >> 8;
+                if (pg->surface_shape.z_format) {
+                    gl_clear_depth = convert_f24_to_float(z) / f24_max;
+                    assert(false); /* FIXME: Untested */
+                } else {
+                    gl_clear_depth = z / (float)0xFFFFFF;
+                }
+                break;
+            }
+            default:
+                assert(false);
+                break;
             }
             if (parameter & NV097_CLEAR_SURFACE_Z) {
                 gl_mask |= GL_DEPTH_BUFFER_BIT;