st/nine: Implement MSAA quality levels
authorPatrick Rudolph <siro@das-labor.org>
Thu, 22 Sep 2016 15:03:17 +0000 (17:03 +0200)
committerAxel Davy <axel.davy@ens.fr>
Mon, 10 Oct 2016 21:43:51 +0000 (23:43 +0200)
Advertise quality levels:
Each supported multisample count matches to one quality level.
The application doesn't know how much samples each quality level has.
For that reason it's not possible to set the multisample mask.

Return errors on quality level missmatch.

Fixes several old games not having multisample support until now.

Fix for issue #73.

Signed-off-by: Patrick Rudolph <siro@das-labor.org>
Signed-off-by: Axel Davy <axel.davy@ens.fr>
src/gallium/state_trackers/nine/adapter9.c
src/gallium/state_trackers/nine/device9.c
src/gallium/state_trackers/nine/nine_pipe.h
src/gallium/state_trackers/nine/nine_state.c
src/gallium/state_trackers/nine/surface9.c
src/gallium/state_trackers/nine/surface9.h
src/gallium/state_trackers/nine/swapchain9.c

index fbbc586204afcce8d87f3468e995b518f5054a4d..09bfa3989ff76266ca0786ae98f7e9188668d522 100644 (file)
@@ -400,17 +400,31 @@ NineAdapter9_CheckDeviceMultiSampleType( struct NineAdapter9 *This,
     else /* render-target */
         bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
 
+    pf = d3d9_to_pipe_format_checked(screen, SurfaceFormat, PIPE_TEXTURE_2D,
+                                     0, PIPE_BIND_SAMPLER_VIEW, FALSE, FALSE);
+
+    if (pf == PIPE_FORMAT_NONE && SurfaceFormat != D3DFMT_NULL) {
+        DBG("%s not available.\n", d3dformat_to_string(SurfaceFormat));
+        return D3DERR_INVALIDCALL;
+    }
+
     pf = d3d9_to_pipe_format_checked(screen, SurfaceFormat, PIPE_TEXTURE_2D,
                                      MultiSampleType, bind, FALSE, FALSE);
 
-    if (pf == PIPE_FORMAT_NONE) {
+    if (pf == PIPE_FORMAT_NONE && SurfaceFormat != D3DFMT_NULL) {
         DBG("%s with %u samples not available.\n",
             d3dformat_to_string(SurfaceFormat), MultiSampleType);
         return D3DERR_NOTAVAILABLE;
     }
 
-    if (pQualityLevels)
-        *pQualityLevels = 1; /* gallium doesn't have quality levels */
+    if (pQualityLevels) {
+        /* NONMASKABLE MultiSampleType might have more than one quality level,
+         * while MASKABLE MultiSampleTypes have only one level.
+         * Advertise quality levels and map each level to a sample count. */
+         (void ) d3dmultisample_type_check(screen, SurfaceFormat,
+                 &MultiSampleType, D3DMULTISAMPLE_16_SAMPLES, pQualityLevels);
+         DBG("advertising %u quality levels\n", *pQualityLevels);
+    }
 
     return D3D_OK;
 }
index 50565b876ddcc910f72f20559d1c2cf0fe0e5f38..47fd3a48b55ad9d9db68fb03e585d6c197b0df60 100644 (file)
@@ -1653,7 +1653,8 @@ NineDevice9_StretchRect( struct NineDevice9 *This,
         clamped = !!xy;
     }
 
-    ms = (dst->desc.MultiSampleType | 1) != (src->desc.MultiSampleType | 1);
+    ms = (dst->desc.MultiSampleType != src->desc.MultiSampleType) ||
+         (dst->desc.MultiSampleQuality != src->desc.MultiSampleQuality);
 
     if (clamped || scaled || (blit.dst.format != blit.src.format) || ms) {
         DBG("using pipe->blit()\n");
@@ -1826,6 +1827,11 @@ NineDevice9_SetRenderTarget( struct NineDevice9 *This,
         This->state.scissor.maxy = rt->desc.Height;
 
         This->state.changed.group |= NINE_STATE_VIEWPORT | NINE_STATE_SCISSOR | NINE_STATE_MULTISAMPLE;
+
+        if (This->state.rt[0] &&
+            (This->state.rt[0]->desc.MultiSampleType == D3DMULTISAMPLE_NONMASKABLE) !=
+            (rt->desc.MultiSampleType == D3DMULTISAMPLE_NONMASKABLE))
+            This->state.changed.group |= NINE_STATE_SAMPLE_MASK;
     }
 
     if (This->state.rt[i] != NineSurface9(pRenderTarget)) {
index bbb148db66cef8360e7fc8b75b9915129236573d..df3b38245c0fe0f0fa7a8b99f0ac707dd9bd022c 100644 (file)
@@ -348,6 +348,61 @@ d3d9_to_pipe_format_checked(struct pipe_screen *screen,
     return PIPE_FORMAT_NONE;
 }
 
+/* The quality levels are vendor dependent, so we set our own.
+ * Every quality level has its own sample count and sample
+ * position matrix.
+ * The exact mapping might differ from system to system but thats OK,
+ * as there's no way to gather more information about quality levels
+ * in D3D9.
+ * In case of NONMASKABLE multisample map every quality-level
+ * to a MASKABLE MultiSampleType:
+ *  0: no MSAA
+ *  1: 2x MSAA
+ *  2: 4x MSAA
+ *  ...
+ *  If the requested quality level is not available to nearest
+ *  matching quality level is used.
+ *  If no multisample is available the function sets
+ *  multisample to D3DMULTISAMPLE_NONE and returns zero.
+ */
+static inline HRESULT
+d3dmultisample_type_check(struct pipe_screen *screen,
+                          D3DFORMAT format,
+                          D3DMULTISAMPLE_TYPE *multisample,
+                          DWORD multisamplequality,
+                          DWORD *levels)
+{
+    unsigned bind, i;
+
+    assert(multisample);
+
+    if (levels)
+        *levels = 1;
+
+    if (*multisample == D3DMULTISAMPLE_NONMASKABLE) {
+        if (depth_stencil_format(format))
+            bind = d3d9_get_pipe_depth_format_bindings(format);
+        else /* render-target */
+            bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
+
+        *multisample = 0;
+        for (i = D3DMULTISAMPLE_2_SAMPLES; i < D3DMULTISAMPLE_16_SAMPLES &&
+            multisamplequality; ++i) {
+            if (d3d9_to_pipe_format_checked(screen, format, PIPE_TEXTURE_2D,
+                    i, bind, FALSE, FALSE) != PIPE_FORMAT_NONE) {
+                multisamplequality--;
+                if (levels)
+                    (*levels)++;
+                *multisample = i;
+            }
+        }
+    }
+    /* Make sure to get an exact match */
+    if (multisamplequality)
+        return D3DERR_INVALIDCALL;
+    return D3D_OK;
+}
+
 static inline const char *
 d3dformat_to_string(D3DFORMAT fmt)
 {
index 66c75812c1511764bd81f6de53e6cc2bfadff23f..ed3c821f3376715c621f0637619cb421723fbff9 100644 (file)
@@ -1088,7 +1088,11 @@ nine_update_state(struct NineDevice9 *device)
             pipe->set_blend_color(pipe, &color);
         }
         if (group & NINE_STATE_SAMPLE_MASK) {
-            pipe->set_sample_mask(pipe, state->rs[D3DRS_MULTISAMPLEMASK]);
+            if (state->rt[0]->desc.MultiSampleType == D3DMULTISAMPLE_NONMASKABLE) {
+                pipe->set_sample_mask(pipe, ~0);
+            } else {
+                pipe->set_sample_mask(pipe, state->rs[D3DRS_MULTISAMPLEMASK]);
+            }
         }
         if (group & NINE_STATE_STENCIL_REF) {
             struct pipe_stencil_ref ref;
index ffa8c2af1bdcf7965793bc7ca7f1cf9a20c2a3a1..dc31bb93786e03c16332b8c4e6227a9cf8ce9d8a 100644 (file)
@@ -58,6 +58,7 @@ NineSurface9_ctor( struct NineSurface9 *This,
     struct pipe_surface *surf;
     struct pipe_context *pipe = pParams->device->pipe;
     bool allocate = !pContainer && pDesc->Format != D3DFMT_NULL;
+    D3DMULTISAMPLE_TYPE multisample_type;
 
     DBG("This=%p pDevice=%p pResource=%p Level=%u Layer=%u pDesc=%p\n",
         This, pParams->device, pResource, Level, Layer, pDesc);
@@ -82,6 +83,18 @@ NineSurface9_ctor( struct NineSurface9 *This,
 
     This->data = (uint8_t *)user_buffer;
 
+    multisample_type = pDesc->MultiSampleType;
+
+    /* Map MultiSampleQuality to MultiSampleType */
+    hr = d3dmultisample_type_check(pParams->device->screen,
+                                   pDesc->Format,
+                                   &multisample_type,
+                                   pDesc->MultiSampleQuality,
+                                   NULL);
+    if (FAILED(hr)) {
+        return hr;
+    }
+
     /* TODO: this is (except width and height) duplicate from
      * container info (in the pContainer case). Some refactoring is
      * needed to avoid duplication */
@@ -92,7 +105,7 @@ NineSurface9_ctor( struct NineSurface9 *This,
     This->base.info.depth0 = 1;
     This->base.info.last_level = 0;
     This->base.info.array_size = 1;
-    This->base.info.nr_samples = pDesc->MultiSampleType;
+    This->base.info.nr_samples = multisample_type;
     This->base.info.usage = PIPE_USAGE_DEFAULT;
     This->base.info.bind = PIPE_BIND_SAMPLER_VIEW; /* StretchRect */
 
@@ -721,7 +734,7 @@ NineSurface9_SetResourceResize( struct NineSurface9 *This,
 
     This->desc.Width = This->base.info.width0 = resource->width0;
     This->desc.Height = This->base.info.height0 = resource->height0;
-    This->desc.MultiSampleType = This->base.info.nr_samples = resource->nr_samples;
+    This->base.info.nr_samples = resource->nr_samples;
 
     This->stride = nine_format_get_stride(This->base.info.format,
                                           This->desc.Width);
index a83e8dd4c71308901169a857913a44c9eb5bcdac..476bc81557a4b061292ed1264beb75010c342caa 100644 (file)
@@ -116,6 +116,13 @@ NineSurface9_SetResource( struct NineSurface9 *This,
     pipe_surface_reference(&This->surface[1], NULL);
 }
 
+static inline void
+NineSurface9_SetMultiSampleType( struct NineSurface9 *This,
+                                 D3DMULTISAMPLE_TYPE mst )
+{
+    This->desc.MultiSampleType = mst;
+}
+
 void
 NineSurface9_SetResourceResize( struct NineSurface9 *This,
                                 struct pipe_resource *resource );
index fcda9c7837edfd7287245c210c230340a5144375..86c9be66ae65da4dc20750624652d335b81811b1 100644 (file)
@@ -120,6 +120,7 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
     BOOL has_present_buffers = FALSE;
     int depth;
     unsigned i, oldBufferCount, newBufferCount;
+    D3DMULTISAMPLE_TYPE multisample_type;
 
     DBG("This=%p pParams=%p\n", This, pParams);
     user_assert(pParams != NULL, E_POINTER);
@@ -204,15 +205,26 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
     newBufferCount = pParams->BackBufferCount +
                      (pParams->SwapEffect != D3DSWAPEFFECT_COPY);
 
+    multisample_type = pParams->MultiSampleType;
+
+    /* Map MultiSampleQuality to MultiSampleType */
+    hr = d3dmultisample_type_check(This->screen, pParams->BackBufferFormat,
+                                   &multisample_type,
+                                   pParams->MultiSampleQuality,
+                                   NULL);
+    if (FAILED(hr)) {
+        return hr;
+    }
+
     pf = d3d9_to_pipe_format_checked(This->screen, pParams->BackBufferFormat,
-                                     PIPE_TEXTURE_2D, pParams->MultiSampleType,
+                                     PIPE_TEXTURE_2D, multisample_type,
                                      PIPE_BIND_RENDER_TARGET, FALSE, FALSE);
 
     if (This->actx->linear_framebuffer ||
         (pf != PIPE_FORMAT_B8G8R8X8_UNORM &&
         pf != PIPE_FORMAT_B8G8R8A8_UNORM) ||
         pParams->SwapEffect != D3DSWAPEFFECT_DISCARD ||
-        pParams->MultiSampleType >= 2 ||
+        multisample_type >= 2 ||
         (This->actx->ref && This->actx->ref == This->screen))
         has_present_buffers = TRUE;
 
@@ -239,7 +251,7 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
     desc.Type = D3DRTYPE_SURFACE;
     desc.Pool = D3DPOOL_DEFAULT;
     desc.MultiSampleType = pParams->MultiSampleType;
-    desc.MultiSampleQuality = 0;
+    desc.MultiSampleQuality = pParams->MultiSampleQuality;
     desc.Width = pParams->BackBufferWidth;
     desc.Height = pParams->BackBufferHeight;
 
@@ -279,7 +291,7 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
 
     for (i = 0; i < newBufferCount; ++i) {
         tmplt.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
-        tmplt.nr_samples = pParams->MultiSampleType;
+        tmplt.nr_samples = multisample_type;
         if (!has_present_buffers)
             tmplt.bind |= NINE_BIND_PRESENTBUFFER_FLAGS;
         tmplt.format = d3d9_to_pipe_format_checked(This->screen,
@@ -297,6 +309,7 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
         if (pParams->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER)
             resource->flags |= NINE_RESOURCE_FLAG_LOCKABLE;
         if (This->buffers[i]) {
+            NineSurface9_SetMultiSampleType(This->buffers[i], desc.MultiSampleType);
             NineSurface9_SetResourceResize(This->buffers[i], resource);
             if (has_present_buffers)
                 pipe_resource_reference(&resource, NULL);
@@ -336,7 +349,7 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
          * If it fails with PIPE_BIND_SAMPLER_VIEW, then the app check for texture support
          * would fail too, so we are fine. */
         tmplt.bind |= PIPE_BIND_SAMPLER_VIEW;
-        tmplt.nr_samples = pParams->MultiSampleType;
+        tmplt.nr_samples = multisample_type;
         tmplt.format = d3d9_to_pipe_format_checked(This->screen,
                                                    pParams->AutoDepthStencilFormat,
                                                    PIPE_TEXTURE_2D,
@@ -362,6 +375,7 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
             return D3DERR_OUTOFVIDEOMEMORY;
         }
         if (This->zsbuf) {
+            NineSurface9_SetMultiSampleType(This->zsbuf, desc.MultiSampleType);
             NineSurface9_SetResourceResize(This->zsbuf, resource);
             pipe_resource_reference(&resource, NULL);
         } else {