Browse Source

Multisampled cube rendertarget on Direct3D9.

Lasse Öörni 9 years ago
parent
commit
c745d2ea52

+ 2 - 1
Source/Urho3D/AngelScript/GraphicsAPI.cpp

@@ -513,6 +513,7 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RenderSurface", "RenderSurface@+ get_linkedRenderTarget() const", asMETHOD(RenderSurface, GetLinkedRenderTarget), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "void set_linkedDepthStencil(RenderSurface@+)", asMETHOD(RenderSurface, SetLinkedDepthStencil), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "RenderSurface@+ get_linkedDepthStencil() const", asMETHOD(RenderSurface, GetLinkedDepthStencil), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "bool get_resolveDirty() const", asMETHOD(RenderSurface, IsResolveDirty), asCALL_THISCALL);
 
     RegisterTexture<Texture2D>(engine, "Texture2D");
     engine->RegisterObjectMethod("Texture2D", "bool SetSize(int, int, uint, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1, bool autoResolve = true)", asMETHOD(Texture2D, SetSize), asCALL_THISCALL);
@@ -532,7 +533,7 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Texture3D", "bool SetData(Image@+, bool useAlpha = false)", asMETHODPR(Texture3D, SetData, (Image*, bool), bool), asCALL_THISCALL);
 
     RegisterTexture<TextureCube>(engine, "TextureCube");
-    engine->RegisterObjectMethod("TextureCube", "bool SetSize(int, uint, TextureUsage usage = TEXTURE_STATIC)", asMETHOD(TextureCube, SetSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("TextureCube", "bool SetSize(int, uint, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1, bool autoResolve = true)", asMETHOD(TextureCube, SetSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("TextureCube", "bool SetData(CubeMapFace, Image@+, bool useAlpha = false)", asMETHODPR(TextureCube, SetData, (CubeMapFace, Image*, bool), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("TextureCube", "Image@+ GetImage(CubeMapFace) const", asFUNCTION(TextureCubeGetImage), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("TextureCube", "RenderSurface@+ get_renderSurfaces(CubeMapFace) const", asMETHOD(TextureCube, GetRenderSurface), asCALL_THISCALL);

+ 57 - 3
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -836,13 +836,18 @@ bool Graphics::ResolveToTexture(Texture2D* texture)
 
     URHO3D_PROFILE(ResolveToTexture);
 
+    // Clear dirty flag already, because if resolve fails it's no use to retry (e.g. on the same frame)
+    RenderSurface* surface = texture->GetRenderSurface();
+    texture->SetResolveDirty(false);
+    surface->SetResolveDirty(false);
+
     RECT rect;
     rect.left = 0;
     rect.top = 0;
     rect.right = texture->GetWidth();
     rect.bottom = texture->GetHeight();
 
-    IDirect3DSurface9* srcSurface = (IDirect3DSurface9*)texture->GetRenderSurface()->GetSurface();
+    IDirect3DSurface9* srcSurface = (IDirect3DSurface9*)surface->GetSurface();
     IDirect3DTexture9* destTexture = (IDirect3DTexture9*)texture->GetGPUObject();
     IDirect3DSurface9* destSurface = 0;
     HRESULT hr = destTexture->GetSurfaceLevel(0, &destSurface);
@@ -863,6 +868,51 @@ bool Graphics::ResolveToTexture(Texture2D* texture)
         return true;
 }
 
+bool Graphics::ResolveToTexture(TextureCube* texture)
+{
+    if (!texture || !texture->GetRenderSurface(FACE_POSITIVE_X) || !texture->GetGPUObject() || texture->GetMultiSample() < 2)
+        return false;
+
+    URHO3D_PROFILE(ResolveToTexture);
+    
+    texture->SetResolveDirty(false);
+
+    RECT rect;
+    rect.left = 0;
+    rect.top = 0;
+    rect.right = texture->GetWidth();
+    rect.bottom = texture->GetHeight();
+
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        // Resolve only the surface(s) that were actually rendered to
+        RenderSurface* surface = texture->GetRenderSurface((CubeMapFace)i);
+        if (!surface->IsResolveDirty())
+            continue;
+
+        surface->SetResolveDirty(false);
+        IDirect3DSurface9* srcSurface = (IDirect3DSurface9*)surface->GetSurface();
+        IDirect3DCubeTexture9* destTexture = (IDirect3DCubeTexture9*)texture->GetGPUObject();
+        IDirect3DSurface9* destSurface = 0;
+        HRESULT hr = destTexture->GetCubeMapSurface((D3DCUBEMAP_FACES)i, 0, &destSurface);
+        if (FAILED(hr))
+        {
+            URHO3D_LOGD3DERROR("Failed to get destination surface for resolve", hr);
+            return false;
+        }
+
+        hr = impl_->device_->StretchRect(srcSurface, &rect, destSurface, &rect, D3DTEXF_NONE);
+        URHO3D_SAFE_RELEASE(destSurface);
+        if (FAILED(hr))
+        {
+            URHO3D_LOGD3DERROR("Failed to resolve to texture", hr);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
 {
     if (!vertexCount)
@@ -1413,7 +1463,8 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
             {
                 if (texture->GetType() == Texture2D::GetTypeStatic())
                     ResolveToTexture(static_cast<Texture2D*>(texture));
-                texture->SetResolveDirty(false);
+                else if (texture->GetType() == TextureCube::GetTypeStatic())
+                    ResolveToTexture(static_cast<TextureCube*>(texture));
             }
         }
     }
@@ -1571,9 +1622,12 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
                 SetTexture(i, textures_[i]->GetBackupTexture());
         }
 
-        // If multisampled, mark the texture needing resolve
+        // If multisampled, mark the texture & surface needing resolve
         if (parentTexture->GetMultiSample() > 1 && parentTexture->GetAutoResolve())
+        {
             parentTexture->SetResolveDirty(true);
+            renderTarget->SetResolveDirty(true);
+        }
     }
 
     // First rendertarget controls sRGB write mode

+ 2 - 1
Source/Urho3D/Graphics/Direct3D9/D3D9RenderSurface.cpp

@@ -38,7 +38,8 @@ RenderSurface::RenderSurface(Texture* parentTexture) :
     parentTexture_(parentTexture),
     surface_(0),
     updateMode_(SURFACE_UPDATEVISIBLE),
-    updateQueued_(false)
+    updateQueued_(false),
+    resolveDirty_(false)
 {
 }
 

+ 1 - 1
Source/Urho3D/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -567,7 +567,7 @@ bool Texture2D::Create()
                 (UINT)width_,
                 (UINT)height_,
                 (D3DFORMAT)format_,
-                (multiSample_ > 1) ? (D3DMULTISAMPLE_TYPE)multiSample_ : D3DMULTISAMPLE_NONE,
+                (D3DMULTISAMPLE_TYPE)multiSample_,
                 0,
                 FALSE,
                 (IDirect3DSurface9**)&renderSurface_->surface_,

+ 46 - 4
Source/Urho3D/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -563,6 +563,22 @@ bool TextureCube::Create()
         break;
     }
 
+    if (multiSample_ > 1)
+    {
+        // Fall back to non-multisampled if unsupported multisampling mode
+        GraphicsImpl* impl = graphics_->GetImpl();
+        if (!impl->CheckMultiSampleSupport((D3DFORMAT)format_, multiSample_))
+        {
+            multiSample_ = 1;
+            autoResolve_ = false;
+        }
+        else if (!autoResolve_)
+        {
+            URHO3D_LOGERROR("Multisampled texture without autoresolve is not supported on Direct3D9");
+            return false;
+        }
+    }
+
     IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
     HRESULT hr = device->CreateCubeTexture(
         (UINT)width_,
@@ -585,10 +601,36 @@ bool TextureCube::Create()
     {
         for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
         {
-            hr = ((IDirect3DCubeTexture9*)object_.ptr_)->GetCubeMapSurface((D3DCUBEMAP_FACES)i, 0,
-                (IDirect3DSurface9**)&renderSurfaces_[i]->surface_);
-            if (FAILED(hr))
-                URHO3D_LOGD3DERROR("Could not get rendertarget surface", hr);
+            if (multiSample_ > 1)
+            {
+                // Create the multisampled face rendertarget if necessary
+                HRESULT hr = device->CreateRenderTarget(
+                    (UINT)width_,
+                    (UINT)height_,
+                    (D3DFORMAT)format_,
+                    (D3DMULTISAMPLE_TYPE)multiSample_,
+                    0,
+                    FALSE,
+                    (IDirect3DSurface9**)&renderSurfaces_[i]->surface_,
+                    0);
+                if (FAILED(hr))
+                {
+                    URHO3D_SAFE_RELEASE(renderSurfaces_[i]->surface_);
+                    URHO3D_LOGD3DERROR("Could not create multisampled rendertarget surface", hr);
+                    return false;
+                }
+            }
+            else
+            {
+                hr = ((IDirect3DCubeTexture9*)object_.ptr_)->GetCubeMapSurface((D3DCUBEMAP_FACES)i, 0,
+                    (IDirect3DSurface9**)&renderSurfaces_[i]->surface_);
+                if (FAILED(hr))
+                {
+                    URHO3D_SAFE_RELEASE(renderSurfaces_[i]->surface_);
+                    URHO3D_LOGD3DERROR("Could not get rendertarget surface", hr);
+                    return false;
+                }
+            }
         }
     }
 

+ 2 - 0
Source/Urho3D/Graphics/Graphics.h

@@ -130,6 +130,8 @@ public:
     bool ResolveToTexture(Texture2D* destination, const IntRect& viewport);
     /// Resolve a multisampled texture on itself.
     bool ResolveToTexture(Texture2D* texture);
+    /// Resolve a multisampled cube texture on itself.
+    bool ResolveToTexture(TextureCube* texture);
     /// Draw non-indexed geometry.
     void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
     /// Draw indexed geometry.

+ 8 - 0
Source/Urho3D/Graphics/RenderSurface.h

@@ -116,6 +116,12 @@ public:
     /// Return OpenGL renderbuffer if created.
     unsigned GetRenderBuffer() const { return renderBuffer_; }
 
+    /// Return whether multisampled rendertarget needs resolve.
+    bool IsResolveDirty() const { return resolveDirty_; }
+
+    /// Set or clear the need resolve flag. Called internally by Graphics.
+    void SetResolveDirty(bool enable) { resolveDirty_ = enable; }
+
 private:
     /// Parent texture.
     Texture* parentTexture_;
@@ -148,6 +154,8 @@ private:
     RenderSurfaceUpdateMode updateMode_;
     /// Update queued flag.
     bool updateQueued_;
+    /// Multisampled resolve dirty flag.
+    bool resolveDirty_;
 };
 
 }

+ 1 - 1
Source/Urho3D/Graphics/Renderer.cpp

@@ -1073,7 +1073,7 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, int m
         else
         {
             SharedPtr<TextureCube> newTexCube(new TextureCube(context_));
-            newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET);
+            newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET, multiSample, autoResolve);
 
             newBuffer = newTexCube;
         }

+ 1 - 1
Source/Urho3D/Graphics/Texture2D.h

@@ -56,7 +56,7 @@ public:
     /// Release the texture.
     virtual void Release();
 
-    /// Set size, format, usage and multisampling parameters for rendertargets. Zero size will follow application window size. Return true if successful. Autoresolve false means that texture will be read as individual samples in the shader and is not supported on Direct3D9.
+    /// Set size, format, usage and multisampling parameters for rendertargets. Zero size will follow application window size. Autoresolve false means that texture will be read as individual samples in the shader and is not supported on Direct3D9. Return true if successful.
     bool SetSize(int width, int height, unsigned format, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1, bool autoResolve = true);
     /// Set data either partially or fully on a mip level. Return true if successful.
     bool SetData(unsigned level, int x, int y, int width, int height, const void* data);

+ 12 - 1
Source/Urho3D/Graphics/TextureCube.cpp

@@ -258,7 +258,7 @@ bool TextureCube::EndLoad()
     return true;
 }
 
-bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
+bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage, int multiSample, bool autoResolve)
 {
     if (size <= 0)
     {
@@ -271,6 +271,15 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
         return false;
     }
 
+    multiSample = Clamp(multiSample, 1, 16);
+    if (multiSample == 1)
+        autoResolve = false;
+    else if (multiSample > 1 && usage < TEXTURE_RENDERTARGET)
+    {
+        URHO3D_LOGERROR("Multisampling is only supported for rendertarget cube maps");
+        return false;
+    }
+
     // Delete the old rendersurfaces if any
     for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
     {
@@ -303,6 +312,8 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
     width_ = size;
     height_ = size;
     format_ = format;
+    multiSample_ = multiSample;
+    autoResolve_ = autoResolve;
 
     return Create();
 }

+ 2 - 2
Source/Urho3D/Graphics/TextureCube.h

@@ -56,8 +56,8 @@ public:
     /// Release the texture.
     virtual void Release();
 
-    /// Set size, format and usage. Return true if successful.
-    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set size, format, usage and multisampling parameters for rendertargets. Autoresolve false means that texture will be read as individual samples in the shader and is not supported on Direct3D9. Return true if successful.
+    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1, bool autoResolve = true);
     /// Set data either partially or fully on a face's mip level. Return true if successful.
     bool SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data);
     /// Set data of one face from a stream. Return true if successful.

+ 2 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/RenderSurface.pkg

@@ -22,6 +22,7 @@ class RenderSurface
     RenderSurfaceUpdateMode GetUpdateMode() const;
     RenderSurface* GetLinkedRenderTarget() const;
     RenderSurface* GetLinkedDepthStencil() const;
+    bool IsResolveDirty() const;
     
     tolua_readonly tolua_property__get_set Texture* parentTexture;
     tolua_readonly tolua_property__get_set int width;
@@ -31,4 +32,5 @@ class RenderSurface
     tolua_property__get_set RenderSurfaceUpdateMode updateMode;
     tolua_property__get_set RenderSurface* linkedRenderTarget;
     tolua_property__get_set RenderSurface* linkedDepthStencil;
+    tolua_readonly tolua_property__is_set bool resolveDirty;
 };

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/Graphics/TextureCube.pkg

@@ -5,7 +5,7 @@ class TextureCube : public Texture
     TextureCube();
     ~TextureCube();
 
-    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1, bool autoResolve = true);
     bool SetData(CubeMapFace face, Image* image, bool useAlpha = false);
 
     RenderSurface* GetRenderSurface(CubeMapFace face) const;