Browse Source

Merge branch 'multisample-textures'

Lasse Öörni 9 years ago
parent
commit
fc42e5d98e
43 changed files with 862 additions and 130 deletions
  1. 15 1
      Docs/Reference.dox
  2. 3 0
      Source/Urho3D/AngelScript/APITemplates.h
  3. 7 2
      Source/Urho3D/AngelScript/GraphicsAPI.cpp
  4. 65 0
      Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.cpp
  5. 1 1
      Source/Urho3D/Graphics/Direct3D11/D3D11RenderSurface.cpp
  6. 32 14
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.cpp
  7. 6 6
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture2DArray.cpp
  8. 5 5
      Source/Urho3D/Graphics/Direct3D11/D3D11Texture3D.cpp
  9. 44 14
      Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.cpp
  10. 109 7
      Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp
  11. 6 0
      Source/Urho3D/Graphics/Direct3D9/D3D9GraphicsImpl.cpp
  12. 3 0
      Source/Urho3D/Graphics/Direct3D9/D3D9GraphicsImpl.h
  13. 3 2
      Source/Urho3D/Graphics/Direct3D9/D3D9RenderSurface.cpp
  14. 52 9
      Source/Urho3D/Graphics/Direct3D9/D3D9Texture2D.cpp
  15. 1 1
      Source/Urho3D/Graphics/Direct3D9/D3D9Texture3D.cpp
  16. 46 9
      Source/Urho3D/Graphics/Direct3D9/D3D9TextureCube.cpp
  17. 5 1
      Source/Urho3D/Graphics/Graphics.h
  18. 195 16
      Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp
  19. 2 0
      Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.cpp
  20. 4 0
      Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.h
  21. 26 5
      Source/Urho3D/Graphics/OpenGL/OGLRenderSurface.cpp
  22. 9 0
      Source/Urho3D/Graphics/OpenGL/OGLTexture.cpp
  23. 41 1
      Source/Urho3D/Graphics/OpenGL/OGLTexture2D.cpp
  24. 16 0
      Source/Urho3D/Graphics/OpenGL/OGLTextureCube.cpp
  25. 5 0
      Source/Urho3D/Graphics/RenderPath.cpp
  26. 6 0
      Source/Urho3D/Graphics/RenderPath.h
  27. 10 0
      Source/Urho3D/Graphics/RenderSurface.cpp
  28. 15 1
      Source/Urho3D/Graphics/RenderSurface.h
  29. 31 10
      Source/Urho3D/Graphics/Renderer.cpp
  30. 10 3
      Source/Urho3D/Graphics/Renderer.h
  31. 5 1
      Source/Urho3D/Graphics/Texture.cpp
  32. 23 0
      Source/Urho3D/Graphics/Texture.h
  33. 12 1
      Source/Urho3D/Graphics/Texture2D.cpp
  34. 5 2
      Source/Urho3D/Graphics/Texture2D.h
  35. 11 2
      Source/Urho3D/Graphics/TextureCube.cpp
  36. 2 2
      Source/Urho3D/Graphics/TextureCube.h
  37. 15 11
      Source/Urho3D/Graphics/View.cpp
  38. 2 0
      Source/Urho3D/LuaScript/pkgs/Graphics/RenderPath.pkg
  39. 2 0
      Source/Urho3D/LuaScript/pkgs/Graphics/RenderSurface.pkg
  40. 4 1
      Source/Urho3D/LuaScript/pkgs/Graphics/Renderer.pkg
  41. 6 0
      Source/Urho3D/LuaScript/pkgs/Graphics/Texture.pkg
  42. 1 1
      Source/Urho3D/LuaScript/pkgs/Graphics/Texture2D.pkg
  43. 1 1
      Source/Urho3D/LuaScript/pkgs/Graphics/TextureCube.pkg

+ 15 - 1
Docs/Reference.dox

@@ -414,6 +414,7 @@ Resources include most things in Urho3D that are loaded from mass storage during
 - Sound
 - Sound
 - Technique
 - Technique
 - Texture2D
 - Texture2D
+- Texture2DArray
 - Texture3D
 - Texture3D
 - TextureCube
 - TextureCube
 - XMLFile
 - XMLFile
@@ -1110,6 +1111,8 @@ OpenGL ES 2.0 has further limitations:
 
 
 - 3D and 2D array textures are not currently supported.
 - 3D and 2D array textures are not currently supported.
 
 
+- Multisampled texture rendertargets are not supported.
+
 \page VertexBuffers Vertex buffers
 \page VertexBuffers Vertex buffers
 
 
 %Geometry data is defined by VertexBuffer objects, which hold a number of vertices of a certain vertex format. For rendering, the data is uploaded to the GPU, but optionally a shadow copy of
 %Geometry data is defined by VertexBuffer objects, which hold a number of vertices of a certain vertex format. For rendering, the data is uploaded to the GPU, but optionally a shadow copy of
@@ -1511,7 +1514,8 @@ The render path XML definition looks like this:
 \code
 \code
 <renderpath>
 <renderpath>
     <rendertarget name="RTName" tag="TagName" enabled="true|false" cubemap="true|false" size="x y"|sizedivisor="x y"|sizemultiplier="x y"
     <rendertarget name="RTName" tag="TagName" enabled="true|false" cubemap="true|false" size="x y"|sizedivisor="x y"|sizemultiplier="x y"
-        format="rgb|rgba|r32f|rgba16|rgba16f|rgba32f|rg16|rg16f|rg32f|lineardepth|readabledepth" filter="true|false" srgb="true|false" persistent="true|false" />
+        format="rgb|rgba|r32f|rgba16|rgba16f|rgba32f|rg16|rg16f|rg32f|lineardepth|readabledepth" filter="true|false" srgb="true|false" persistent="true|false"
+        multisample="x" autoresolve="true|false" />
     <command type="clear" tag="TagName" enabled="true|false" color="r g b a|fog" depth="x" stencil="y" output="viewport|RTName" face="0|1|2|3|4|5" depthstencil="DSName" />
     <command type="clear" tag="TagName" enabled="true|false" color="r g b a|fog" depth="x" stencil="y" output="viewport|RTName" face="0|1|2|3|4|5" depthstencil="DSName" />
     <command type="scenepass" pass="PassName" sort="fronttoback|backtofront" marktostencil="true|false" vertexlights="true|false" metadata="base|alpha|gbuffer" depthstencil="DSName">
     <command type="scenepass" pass="PassName" sort="fronttoback|backtofront" marktostencil="true|false" vertexlights="true|false" metadata="base|alpha|gbuffer" depthstencil="DSName">
         <output index="0" name="RTName1" face="0|1|2|3|4|5" />
         <output index="0" name="RTName1" face="0|1|2|3|4|5" />
@@ -1588,6 +1592,14 @@ Post-processing effects are usually implemented by using the quad command. When
 
 
 In OpenGL post-processing shaders it is important to distinguish between sampling a rendertarget texture and a regular texture resource, because intermediate rendertargets (such as the G-buffer) may be vertically inverted. Use the GetScreenPos() or GetQuadTexCoord() functions to get rendertarget UV coordinates from the clip coordinates; this takes flipping into account automatically. For sampling a regular texture, use GetQuadTexCoordNoFlip() function, which requires world coordinates instead of clip coordinates.
 In OpenGL post-processing shaders it is important to distinguish between sampling a rendertarget texture and a regular texture resource, because intermediate rendertargets (such as the G-buffer) may be vertically inverted. Use the GetScreenPos() or GetQuadTexCoord() functions to get rendertarget UV coordinates from the clip coordinates; this takes flipping into account automatically. For sampling a regular texture, use GetQuadTexCoordNoFlip() function, which requires world coordinates instead of clip coordinates.
 
 
+\section RenderPaths_MultiSample Multisampled rendertargets
+
+Texture2D and TextureCube support multisampling. Programmatically, multisampling is enabled through the \ref Texture2D::SetSize "SetSize()" function when defining the dimensions and format. Multisampling can also be set in a renderpath's rendertarget definition.
+
+The normal operation is that a multisampled rendertarget will be automatically resolved to 1-sample before being sampled as a texture. This is denoted by the \ref Texture2D::GetAutoResolve() "autoResolve" parameter, whose default value is true. On OpenGL (when supported) and Direct3D11, it's also possible to access the individual samples of a Texture2D in shader code by defining a multisampled sampler and using specialized functions (texelFetch on OpenGL, Texture2DMS.Load on Direct3D11). In this case the "autoResolve" parameter should be set to false. Note that accessing individual samples is not possible for cube textures, or when using Direct3D9.
+
+By accessing the individual samples of multisampled G-buffer textures, a deferred MSAA renderer could be implemented. This has some performance considerations / complexities (you should avoid running the lighting calculations per sample when not on triangle edges) and is not implemented by default.
+
 \page Lights Lights and shadows
 \page Lights Lights and shadows
 
 
 Lights in Urho3D can be directional, point, or spot lights, either per-pixel or per-vertex. Shadow mapping is supported for all per-pixel lights.
 Lights in Urho3D can be directional, point, or spot lights, either per-pixel or per-vertex. Shadow mapping is supported for all per-pixel lights.
@@ -1654,6 +1666,8 @@ The shadow map base resolution and quality (bit depth & sampling mode) are set t
 
 
 The shadow quality enum allows choosing also variance (VSM) shadows instead of the default hardware depth shadows. VSM shadows behave markedly differently; depth bias settings are no longer relevant, but you should make sure all your large surfaces (also ground & terrain) are marked as shadow casters, otherwise shadows cast by objects moving over them can appear unnaturally thin. For VSM shadows, see the functions \ref Renderer::SetShadowSoftness "SetShadowSoftness()" and \ref Renderer::SetVSMShadowParameters "SetVSMShadowParameters()" to control the softness (blurring) and in-shadow detection behavior. Instead of self-shadowing artifacts common with hardware depth shadows, you may encounter light bleeding when shadow casting surfaces are close in light direction to each other, which adjusting the VSM shadow parameters may help.
 The shadow quality enum allows choosing also variance (VSM) shadows instead of the default hardware depth shadows. VSM shadows behave markedly differently; depth bias settings are no longer relevant, but you should make sure all your large surfaces (also ground & terrain) are marked as shadow casters, otherwise shadows cast by objects moving over them can appear unnaturally thin. For VSM shadows, see the functions \ref Renderer::SetShadowSoftness "SetShadowSoftness()" and \ref Renderer::SetVSMShadowParameters "SetVSMShadowParameters()" to control the softness (blurring) and in-shadow detection behavior. Instead of self-shadowing artifacts common with hardware depth shadows, you may encounter light bleeding when shadow casting surfaces are close in light direction to each other, which adjusting the VSM shadow parameters may help.
 
 
+VSM shadow maps can also be multisampled for better quality, though this has a performance cost. See \ref Renderer::SetVSMMultiSample "SetVSMMultiSample()".
+
 \section Lights_ShadowMapReuse Shadow map reuse
 \section Lights_ShadowMapReuse Shadow map reuse
 
 
 The Renderer can be configured to either reuse shadow maps, or not. To reuse is the default, use \ref Renderer::SetReuseShadowMaps "SetReuseShadowMaps()" to change.
 The Renderer can be configured to either reuse shadow maps, or not. To reuse is the default, use \ref Renderer::SetReuseShadowMaps "SetReuseShadowMaps()" to change.

+ 3 - 0
Source/Urho3D/AngelScript/APITemplates.h

@@ -902,6 +902,9 @@ template <class T> void RegisterTexture(asIScriptEngine* engine, const char* cla
     engine->RegisterObjectMethod(className, "const Color& get_borderColor() const", asMETHOD(T, GetBorderColor), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Color& get_borderColor() const", asMETHOD(T, GetBorderColor), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_sRGB(bool)", asMETHOD(T, SetSRGB), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_sRGB(bool)", asMETHOD(T, SetSRGB), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_sRGB() const", asMETHOD(T, GetSRGB), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_sRGB() const", asMETHOD(T, GetSRGB), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "int get_multiSample() const", asMETHOD(T, GetMultiSample), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool get_autoResolve() const", asMETHOD(T, GetAutoResolve), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool get_resolveDirty() const", asMETHOD(T, IsResolveDirty), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_backupTexture(Texture@+)", asMETHOD(T, SetBackupTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_backupTexture(Texture@+)", asMETHOD(T, SetBackupTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Texture@+ get_backupTexture() const", asMETHOD(T, GetBackupTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Texture@+ get_backupTexture() const", asMETHOD(T, GetBackupTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_mipsToSkip(int, int)", asMETHOD(T, SetMipsToSkip), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_mipsToSkip(int, int)", asMETHOD(T, SetMipsToSkip), asCALL_THISCALL);

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

@@ -367,6 +367,8 @@ static void RegisterRenderPath(asIScriptEngine* engine)
     engine->RegisterObjectProperty("RenderTargetInfo", "uint format", offsetof(RenderTargetInfo, format_));
     engine->RegisterObjectProperty("RenderTargetInfo", "uint format", offsetof(RenderTargetInfo, format_));
     engine->RegisterObjectProperty("RenderTargetInfo", "Vector2 size", offsetof(RenderTargetInfo, size_));
     engine->RegisterObjectProperty("RenderTargetInfo", "Vector2 size", offsetof(RenderTargetInfo, size_));
     engine->RegisterObjectProperty("RenderTargetInfo", "RenderTargetSizeMode sizeMode", offsetof(RenderTargetInfo, sizeMode_));
     engine->RegisterObjectProperty("RenderTargetInfo", "RenderTargetSizeMode sizeMode", offsetof(RenderTargetInfo, sizeMode_));
+    engine->RegisterObjectProperty("RenderTargetInfo", "int multiSample", offsetof(RenderTargetInfo, multiSample_));
+    engine->RegisterObjectProperty("RenderTargetInfo", "bool autoResolve", offsetof(RenderTargetInfo, autoResolve_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool enabled", offsetof(RenderTargetInfo, enabled_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool enabled", offsetof(RenderTargetInfo, enabled_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool cubemap", offsetof(RenderTargetInfo, cubemap_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool cubemap", offsetof(RenderTargetInfo, cubemap_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool filtered", offsetof(RenderTargetInfo, filtered_));
     engine->RegisterObjectProperty("RenderTargetInfo", "bool filtered", offsetof(RenderTargetInfo, filtered_));
@@ -511,9 +513,10 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterObjectMethod("RenderSurface", "RenderSurface@+ get_linkedRenderTarget() const", asMETHOD(RenderSurface, GetLinkedRenderTarget), asCALL_THISCALL);
     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", "void set_linkedDepthStencil(RenderSurface@+)", asMETHOD(RenderSurface, SetLinkedDepthStencil), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "RenderSurface@+ get_linkedDepthStencil() const", asMETHOD(RenderSurface, GetLinkedDepthStencil), 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");
     RegisterTexture<Texture2D>(engine, "Texture2D");
-    engine->RegisterObjectMethod("Texture2D", "bool SetSize(int, int, uint, TextureUsage usage = TEXTURE_STATIC)", asMETHOD(Texture2D, SetSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Texture2D", "bool SetSize(int, int, uint, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1, bool autoResolve = true)", asMETHOD(Texture2D, SetSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("Texture2D", "bool SetData(Image@+, bool useAlpha = false)", asMETHODPR(Texture2D, SetData, (Image*, bool), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("Texture2D", "bool SetData(Image@+, bool useAlpha = false)", asMETHODPR(Texture2D, SetData, (Image*, bool), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("Texture2D", "RenderSurface@+ get_renderSurface() const", asMETHOD(Texture2D, GetRenderSurface), asCALL_THISCALL);
     engine->RegisterObjectMethod("Texture2D", "RenderSurface@+ get_renderSurface() const", asMETHOD(Texture2D, GetRenderSurface), asCALL_THISCALL);
     engine->RegisterObjectMethod("Texture2D", "Image@+ GetImage() const", asFUNCTION(Texture2DGetImage), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Texture2D", "Image@+ GetImage() const", asFUNCTION(Texture2DGetImage), asCALL_CDECL_OBJLAST);
@@ -530,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);
     engine->RegisterObjectMethod("Texture3D", "bool SetData(Image@+, bool useAlpha = false)", asMETHODPR(Texture3D, SetData, (Image*, bool), bool), asCALL_THISCALL);
 
 
     RegisterTexture<TextureCube>(engine, "TextureCube");
     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)", 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", "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", "Image@+ GetImage(CubeMapFace) const", asFUNCTION(TextureCubeGetImage), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("TextureCube", "RenderSurface@+ get_renderSurfaces(CubeMapFace) const", asMETHOD(TextureCube, GetRenderSurface), asCALL_THISCALL);
     engine->RegisterObjectMethod("TextureCube", "RenderSurface@+ get_renderSurfaces(CubeMapFace) const", asMETHOD(TextureCube, GetRenderSurface), asCALL_THISCALL);
@@ -1945,6 +1948,8 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "float get_shadowSoftness() const", asMETHOD(Renderer, GetShadowSoftness), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "float get_shadowSoftness() const", asMETHOD(Renderer, GetShadowSoftness), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_vsmShadowParameters(const Vector2&in)", asFUNCTION(RendererSetVSMShadowParameters), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Renderer", "void set_vsmShadowParameters(const Vector2&in)", asFUNCTION(RendererSetVSMShadowParameters), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Renderer", "Vector2 get_vsmShadowParameters() const", asMETHOD(Renderer, GetVSMShadowParameters), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "Vector2 get_vsmShadowParameters() const", asMETHOD(Renderer, GetVSMShadowParameters), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_vsmMultiSample(int)", asMETHOD(Renderer, SetVSMMultiSample), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "int get_vsmMultiSample() const", asMETHOD(Renderer, GetVSMMultiSample), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxShadowMaps(int)", asMETHOD(Renderer, SetMaxShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxShadowMaps(int)", asMETHOD(Renderer, SetMaxShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxShadowMaps() const", asMETHOD(Renderer, GetMaxShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxShadowMaps() const", asMETHOD(Renderer, GetMaxShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_reuseShadowMaps(bool)", asMETHOD(Renderer, SetReuseShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_reuseShadowMaps(bool)", asMETHOD(Renderer, SetReuseShadowMaps), asCALL_THISCALL);

+ 65 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -36,6 +36,7 @@
 #include "../../Graphics/ShaderPrecache.h"
 #include "../../Graphics/ShaderPrecache.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/Texture2D.h"
 #include "../../Graphics/Texture2D.h"
+#include "../../Graphics/TextureCube.h"
 #include "../../Graphics/VertexBuffer.h"
 #include "../../Graphics/VertexBuffer.h"
 #include "../../IO/File.h"
 #include "../../IO/File.h"
 #include "../../IO/Log.h"
 #include "../../IO/Log.h"
@@ -730,6 +731,52 @@ bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
     return true;
     return true;
 }
 }
 
 
+bool Graphics::ResolveToTexture(Texture2D* texture)
+{
+    if (!texture)
+        return false;
+    RenderSurface* surface = texture->GetRenderSurface();
+    if (!surface)
+        return false;
+
+    texture->SetResolveDirty(false);
+    surface->SetResolveDirty(false);
+    ID3D11Resource* source = (ID3D11Resource*)texture->GetGPUObject();
+    ID3D11Resource* dest = (ID3D11Resource*)texture->GetResolveTexture();
+    if (!source || !dest)
+        return false;
+
+    impl_->deviceContext_->ResolveSubresource(dest, 0, source, 0, (DXGI_FORMAT)texture->GetFormat());
+    return true;
+}
+
+bool Graphics::ResolveToTexture(TextureCube* texture)
+{
+    if (!texture)
+        return false;
+
+    texture->SetResolveDirty(false);
+    ID3D11Resource* source = (ID3D11Resource*)texture->GetGPUObject();
+    ID3D11Resource* dest = (ID3D11Resource*)texture->GetResolveTexture();
+    if (!source || !dest)
+        return false;
+
+    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);
+        unsigned subResource = D3D11CalcSubresource(0, i, texture->GetLevels());
+        impl_->deviceContext_->ResolveSubresource(dest, subResource, source, subResource, (DXGI_FORMAT)texture->GetFormat());
+    }
+
+    return true;
+}
+
+
 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
 {
 {
     if (!vertexCount || !impl_->shaderProgram_)
     if (!vertexCount || !impl_->shaderProgram_)
@@ -1281,6 +1328,17 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
     {
     {
         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
             texture = texture->GetBackupTexture();
             texture = texture->GetBackupTexture();
+        else
+        {
+            // Resolve multisampled texture now as necessary
+            if (texture->GetMultiSample() > 1 && texture->GetAutoResolve() && texture->IsResolveDirty())
+            {
+                if (texture->GetType() == Texture2D::GetTypeStatic())
+                    ResolveToTexture(static_cast<Texture2D*>(texture));
+                if (texture->GetType() == TextureCube::GetTypeStatic())
+                    ResolveToTexture(static_cast<TextureCube*>(texture));
+            }
+        }
     }
     }
 
 
     if (texture && texture->GetParametersDirty())
     if (texture && texture->GetParametersDirty())
@@ -1388,6 +1446,13 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
                 if (textures_[i] == parentTexture)
                 if (textures_[i] == parentTexture)
                     SetTexture(i, textures_[i]->GetBackupTexture());
                     SetTexture(i, textures_[i]->GetBackupTexture());
             }
             }
+            
+            // If multisampled, mark the texture & surface needing resolve
+            if (parentTexture->GetMultiSample() > 1 && parentTexture->GetAutoResolve())
+            {
+                parentTexture->SetResolveDirty(true);
+                renderTarget->SetResolveDirty(true);
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 1
Source/Urho3D/Graphics/Direct3D11/D3D11RenderSurface.cpp

@@ -62,7 +62,7 @@ void RenderSurface::Release()
     URHO3D_SAFE_RELEASE(readOnlyView_);
     URHO3D_SAFE_RELEASE(readOnlyView_);
 }
 }
 
 
-bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format)
+bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format, int multiSample)
 {
 {
     // Not used on Direct3D
     // Not used on Direct3D
     return false;
     return false;

+ 32 - 14
Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.cpp

@@ -64,6 +64,7 @@ void Texture2D::Release()
         renderSurface_->Release();
         renderSurface_->Release();
 
 
     URHO3D_SAFE_RELEASE(object_.ptr_);
     URHO3D_SAFE_RELEASE(object_.ptr_);
+    URHO3D_SAFE_RELEASE(resolveTexture_);
     URHO3D_SAFE_RELEASE(shaderResourceView_);
     URHO3D_SAFE_RELEASE(shaderResourceView_);
     URHO3D_SAFE_RELEASE(sampler_);
     URHO3D_SAFE_RELEASE(sampler_);
 }
 }
@@ -317,8 +318,8 @@ bool Texture2D::GetData(unsigned level, void* dest) const
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(stagingTexture);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
 
 
@@ -342,7 +343,7 @@ bool Texture2D::GetData(unsigned level, void* dest) const
     if (FAILED(hr) || !mappedData.pData)
     if (FAILED(hr) || !mappedData.pData)
     {
     {
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
     else
     else
@@ -350,7 +351,7 @@ bool Texture2D::GetData(unsigned level, void* dest) const
         for (unsigned row = 0; row < numRows; ++row)
         for (unsigned row = 0; row < numRows; ++row)
             memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
             memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return true;
         return true;
     }
     }
 }
 }
@@ -371,8 +372,8 @@ bool Texture2D::Create()
     textureDesc.MipLevels = levels_;
     textureDesc.MipLevels = levels_;
     textureDesc.ArraySize = 1;
     textureDesc.ArraySize = 1;
     textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
     textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
-    textureDesc.SampleDesc.Count = 1;
-    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.SampleDesc.Count = (UINT)multiSample_;
+    textureDesc.SampleDesc.Quality = multiSample_ > 1 ? 0xffffffff : 0;
     textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
     textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
     textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
     textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
     if (usage_ == TEXTURE_RENDERTARGET)
     if (usage_ == TEXTURE_RENDERTARGET)
@@ -384,23 +385,40 @@ bool Texture2D::Create()
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(object_.ptr_);
         URHO3D_LOGD3DERROR("Failed to create texture", hr);
         URHO3D_LOGD3DERROR("Failed to create texture", hr);
+        URHO3D_SAFE_RELEASE(object_.ptr_);
         return false;
         return false;
     }
     }
 
 
+    // Create resolve texture for multisampling if necessary
+    if (multiSample_ > 1 && autoResolve_)
+    {
+        textureDesc.SampleDesc.Count = 1;
+        textureDesc.SampleDesc.Quality = 0;
+        HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&resolveTexture_);
+        if (FAILED(hr))
+        {
+            URHO3D_LOGD3DERROR("Failed to create resolve texture", hr);
+            URHO3D_SAFE_RELEASE(resolveTexture_);
+            return false;
+        }
+    }
+
     D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
     D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
     memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
     memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
     resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
     resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
-    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    resourceViewDesc.ViewDimension = (multiSample_ > 1 && !autoResolve_) ? D3D11_SRV_DIMENSION_TEXTURE2DMS :
+        D3D11_SRV_DIMENSION_TEXTURE2D;
     resourceViewDesc.Texture2D.MipLevels = (UINT)levels_;
     resourceViewDesc.Texture2D.MipLevels = (UINT)levels_;
 
 
-    hr = graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_.ptr_, &resourceViewDesc,
+    // Sample the resolve texture if created, otherwise the original
+    ID3D11Resource* viewObject = resolveTexture_ ? (ID3D11Resource*)resolveTexture_ : (ID3D11Resource*)object_.ptr_;
+    hr = graphics_->GetImpl()->GetDevice()->CreateShaderResourceView(viewObject, &resourceViewDesc,
         (ID3D11ShaderResourceView**)&shaderResourceView_);
         (ID3D11ShaderResourceView**)&shaderResourceView_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(shaderResourceView_);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture", hr);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture", hr);
+        URHO3D_SAFE_RELEASE(shaderResourceView_);
         return false;
         return false;
     }
     }
 
 
@@ -409,14 +427,14 @@ bool Texture2D::Create()
         D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
         D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
         memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
         memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
         renderTargetViewDesc.Format = textureDesc.Format;
         renderTargetViewDesc.Format = textureDesc.Format;
-        renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+        renderTargetViewDesc.ViewDimension = multiSample_ > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
 
 
         hr = graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_.ptr_, &renderTargetViewDesc,
         hr = graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_.ptr_, &renderTargetViewDesc,
             (ID3D11RenderTargetView**)&renderSurface_->renderTargetView_);
             (ID3D11RenderTargetView**)&renderSurface_->renderTargetView_);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(renderSurface_->renderTargetView_);
             URHO3D_LOGD3DERROR("Failed to create rendertarget view for texture", hr);
             URHO3D_LOGD3DERROR("Failed to create rendertarget view for texture", hr);
+            URHO3D_SAFE_RELEASE(renderSurface_->renderTargetView_);
             return false;
             return false;
         }
         }
     }
     }
@@ -425,14 +443,14 @@ bool Texture2D::Create()
         D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
         D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
         memset(&depthStencilViewDesc, 0, sizeof depthStencilViewDesc);
         memset(&depthStencilViewDesc, 0, sizeof depthStencilViewDesc);
         depthStencilViewDesc.Format = (DXGI_FORMAT)GetDSVFormat(textureDesc.Format);
         depthStencilViewDesc.Format = (DXGI_FORMAT)GetDSVFormat(textureDesc.Format);
-        depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
+        depthStencilViewDesc.ViewDimension = multiSample_ > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D;
 
 
         hr = graphics_->GetImpl()->GetDevice()->CreateDepthStencilView((ID3D11Resource*)object_.ptr_, &depthStencilViewDesc,
         hr = graphics_->GetImpl()->GetDevice()->CreateDepthStencilView((ID3D11Resource*)object_.ptr_, &depthStencilViewDesc,
             (ID3D11DepthStencilView**)&renderSurface_->renderTargetView_);
             (ID3D11DepthStencilView**)&renderSurface_->renderTargetView_);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(renderSurface_->renderTargetView_);
             URHO3D_LOGD3DERROR("Failed to create depth-stencil view for texture", hr);
             URHO3D_LOGD3DERROR("Failed to create depth-stencil view for texture", hr);
+            URHO3D_SAFE_RELEASE(renderSurface_->renderTargetView_);
             return false;
             return false;
         }
         }
 
 
@@ -442,8 +460,8 @@ bool Texture2D::Create()
             (ID3D11DepthStencilView**)&renderSurface_->readOnlyView_);
             (ID3D11DepthStencilView**)&renderSurface_->readOnlyView_);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(renderSurface_->readOnlyView_);
             URHO3D_LOGD3DERROR("Failed to create read-only depth-stencil view for texture", hr);
             URHO3D_LOGD3DERROR("Failed to create read-only depth-stencil view for texture", hr);
+            URHO3D_SAFE_RELEASE(renderSurface_->readOnlyView_);
         }
         }
     }
     }
 
 

+ 6 - 6
Source/Urho3D/Graphics/Direct3D11/D3D11Texture2DArray.cpp

@@ -392,8 +392,8 @@ bool Texture2DArray::GetData(unsigned layer, unsigned level, void* dest) const
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(stagingTexture);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
 
 
@@ -417,7 +417,7 @@ bool Texture2DArray::GetData(unsigned layer, unsigned level, void* dest) const
     if (FAILED(hr) || !mappedData.pData)
     if (FAILED(hr) || !mappedData.pData)
     {
     {
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
     else
     else
@@ -425,7 +425,7 @@ bool Texture2DArray::GetData(unsigned layer, unsigned level, void* dest) const
         for (unsigned row = 0; row < numRows; ++row)
         for (unsigned row = 0; row < numRows; ++row)
             memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
             memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return true;
         return true;
     }
     }
 }
 }
@@ -459,8 +459,8 @@ bool Texture2DArray::Create()
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(object_.ptr_);
         URHO3D_LOGD3DERROR("Failed to create texture array", hr);
         URHO3D_LOGD3DERROR("Failed to create texture array", hr);
+        URHO3D_SAFE_RELEASE(object_.ptr_);
         return false;
         return false;
     }
     }
 
 
@@ -486,8 +486,8 @@ bool Texture2DArray::Create()
         (ID3D11ShaderResourceView**)&shaderResourceView_);
         (ID3D11ShaderResourceView**)&shaderResourceView_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(shaderResourceView_);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture array", hr);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture array", hr);
+        URHO3D_SAFE_RELEASE(shaderResourceView_);
         return false;
         return false;
     }
     }
 
 
@@ -514,8 +514,8 @@ bool Texture2DArray::Create()
 
 
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(renderSurface_->renderTargetView_);
             URHO3D_LOGD3DERROR("Failed to create rendertarget view for texture array", hr);
             URHO3D_LOGD3DERROR("Failed to create rendertarget view for texture array", hr);
+            URHO3D_SAFE_RELEASE(renderSurface_->renderTargetView_);
             return false;
             return false;
         }
         }
     }
     }

+ 5 - 5
Source/Urho3D/Graphics/Direct3D11/D3D11Texture3D.cpp

@@ -330,8 +330,8 @@ bool Texture3D::GetData(unsigned level, void* dest) const
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, &stagingTexture);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, &stagingTexture);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(stagingTexture);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
 
 
@@ -355,7 +355,7 @@ bool Texture3D::GetData(unsigned level, void* dest) const
     if (FAILED(hr) || !mappedData.pData)
     if (FAILED(hr) || !mappedData.pData)
     {
     {
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
     else
     else
@@ -369,7 +369,7 @@ bool Texture3D::GetData(unsigned level, void* dest) const
             }
             }
         }
         }
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return true;
         return true;
     }
     }
 }
 }
@@ -397,8 +397,8 @@ bool Texture3D::Create()
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, (ID3D11Texture3D**)&object_.ptr_);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, (ID3D11Texture3D**)&object_.ptr_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(object_.ptr_);
         URHO3D_LOGD3DERROR("Failed to create texture", hr);
         URHO3D_LOGD3DERROR("Failed to create texture", hr);
+        URHO3D_SAFE_RELEASE(object_.ptr_);
         return false;
         return false;
     }
     }
 
 
@@ -412,8 +412,8 @@ bool Texture3D::Create()
         (ID3D11ShaderResourceView**)&shaderResourceView_);
         (ID3D11ShaderResourceView**)&shaderResourceView_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(shaderResourceView_);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture", hr);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture", hr);
+        URHO3D_SAFE_RELEASE(shaderResourceView_);
         return false;
         return false;
     }
     }
 
 

+ 44 - 14
Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.cpp

@@ -71,6 +71,7 @@ void TextureCube::Release()
     }
     }
 
 
     URHO3D_SAFE_RELEASE(object_.ptr_);
     URHO3D_SAFE_RELEASE(object_.ptr_);
+    URHO3D_SAFE_RELEASE(resolveTexture_);
     URHO3D_SAFE_RELEASE(shaderResourceView_);
     URHO3D_SAFE_RELEASE(shaderResourceView_);
     URHO3D_SAFE_RELEASE(sampler_);
     URHO3D_SAFE_RELEASE(sampler_);
 }
 }
@@ -384,8 +385,8 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(stagingTexture);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to create staging texture for GetData", hr);
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
 
 
@@ -409,7 +410,7 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
     if (FAILED(hr) || !mappedData.pData)
     if (FAILED(hr) || !mappedData.pData)
     {
     {
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
         URHO3D_LOGD3DERROR("Failed to map staging texture for GetData", hr);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return false;
         return false;
     }
     }
     else
     else
@@ -417,7 +418,7 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
         for (unsigned row = 0; row < numRows; ++row)
         for (unsigned row = 0; row < numRows; ++row)
             memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
             memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
         graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
-        stagingTexture->Release();
+        URHO3D_SAFE_RELEASE(stagingTexture);
         return true;
         return true;
     }
     }
 }
 }
@@ -438,8 +439,8 @@ bool TextureCube::Create()
     textureDesc.MipLevels = levels_;
     textureDesc.MipLevels = levels_;
     textureDesc.ArraySize = MAX_CUBEMAP_FACES;
     textureDesc.ArraySize = MAX_CUBEMAP_FACES;
     textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
     textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
-    textureDesc.SampleDesc.Count = 1;
-    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.SampleDesc.Count = (UINT)multiSample_;
+    textureDesc.SampleDesc.Quality = multiSample_ > 1 ? 0xffffffff : 0;
     textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
     textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
     textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
     textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
     if (usage_ == TEXTURE_RENDERTARGET)
     if (usage_ == TEXTURE_RENDERTARGET)
@@ -447,28 +448,48 @@ bool TextureCube::Create()
     else if (usage_ == TEXTURE_DEPTHSTENCIL)
     else if (usage_ == TEXTURE_DEPTHSTENCIL)
         textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
         textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
     textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
     textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
-    textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+    // When multisample is specified, creating an actual cube texture will fail. Rather create as a 2D texture array
+    // whose faces will be rendered to; only the resolve texture will be an actual cube texture
+    if (multiSample_ < 2)
+        textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
 
 
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_.ptr_);
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_.ptr_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(object_.ptr_);
         URHO3D_LOGD3DERROR("Failed to create texture", hr);
         URHO3D_LOGD3DERROR("Failed to create texture", hr);
+        URHO3D_SAFE_RELEASE(object_.ptr_);
         return false;
         return false;
     }
     }
 
 
+    // Create resolve texture for multisampling
+    if (multiSample_ > 1)
+    {
+        textureDesc.SampleDesc.Count = 1;
+        textureDesc.SampleDesc.Quality = 0;
+        textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+        HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&resolveTexture_);
+        if (FAILED(hr))
+        {
+            URHO3D_LOGD3DERROR("Failed to create resolve texture", hr);
+            URHO3D_SAFE_RELEASE(resolveTexture_);
+            return false;
+        }
+    }
+
     D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
     D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
     memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
     memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
     resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
     resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
     resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
     resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
     resourceViewDesc.Texture2D.MipLevels = (UINT)levels_;
     resourceViewDesc.Texture2D.MipLevels = (UINT)levels_;
 
 
-    hr = graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_.ptr_, &resourceViewDesc,
+    // Sample the resolve texture if created, otherwise the original
+    ID3D11Resource* viewObject = resolveTexture_ ? (ID3D11Resource*)resolveTexture_ : (ID3D11Resource*)object_.ptr_;
+    hr = graphics_->GetImpl()->GetDevice()->CreateShaderResourceView(viewObject, &resourceViewDesc,
         (ID3D11ShaderResourceView**)&shaderResourceView_);
         (ID3D11ShaderResourceView**)&shaderResourceView_);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(shaderResourceView_);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture", hr);
         URHO3D_LOGD3DERROR("Failed to create shader resource view for texture", hr);
+        URHO3D_SAFE_RELEASE(shaderResourceView_);
         return false;
         return false;
     }
     }
 
 
@@ -479,18 +500,27 @@ bool TextureCube::Create()
             D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
             D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
             memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
             memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
             renderTargetViewDesc.Format = textureDesc.Format;
             renderTargetViewDesc.Format = textureDesc.Format;
-            renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
-            renderTargetViewDesc.Texture2DArray.ArraySize = 1;
-            renderTargetViewDesc.Texture2DArray.FirstArraySlice = i;
-            renderTargetViewDesc.Texture2DArray.MipSlice = 0;
+            if (multiSample_ > 1)
+            {
+                renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
+                renderTargetViewDesc.Texture2DMSArray.ArraySize = 1;
+                renderTargetViewDesc.Texture2DMSArray.FirstArraySlice = i;
+            }
+            else
+            {
+                renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+                renderTargetViewDesc.Texture2DArray.ArraySize = 1;
+                renderTargetViewDesc.Texture2DArray.FirstArraySlice = i;
+                renderTargetViewDesc.Texture2DArray.MipSlice = 0;
+            }
 
 
             hr = graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_.ptr_, &renderTargetViewDesc,
             hr = graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_.ptr_, &renderTargetViewDesc,
                 (ID3D11RenderTargetView**)&renderSurfaces_[i]->renderTargetView_);
                 (ID3D11RenderTargetView**)&renderSurfaces_[i]->renderTargetView_);
 
 
             if (FAILED(hr))
             if (FAILED(hr))
             {
             {
-                URHO3D_SAFE_RELEASE(renderSurfaces_[i]->renderTargetView_);
                 URHO3D_LOGD3DERROR("Failed to create rendertarget view for texture", hr);
                 URHO3D_LOGD3DERROR("Failed to create rendertarget view for texture", hr);
+                URHO3D_SAFE_RELEASE(renderSurfaces_[i]->renderTargetView_);
                 return false;
                 return false;
             }
             }
         }
         }

+ 109 - 7
Source/Urho3D/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -409,8 +409,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
     // Fall back to non-multisampled if unsupported multisampling mode
     // Fall back to non-multisampled if unsupported multisampling mode
     if (multiSample > 1)
     if (multiSample > 1)
     {
     {
-        if (FAILED(impl_->interface_->CheckDeviceMultiSampleType(impl_->adapter_, impl_->deviceType_, fullscreenFormat, FALSE,
-            (D3DMULTISAMPLE_TYPE)multiSample, NULL)))
+        if (!impl_->CheckMultiSampleSupport(fullscreenFormat, multiSample))
             multiSample = 1;
             multiSample = 1;
     }
     }
 
 
@@ -830,6 +829,92 @@ bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
         return true;
         return true;
 }
 }
 
 
+bool Graphics::ResolveToTexture(Texture2D* texture)
+{
+    if (!texture || !texture->GetRenderSurface() || !texture->GetGPUObject() || texture->GetMultiSample() < 2)
+        return false;
+
+    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*)surface->GetSurface();
+    IDirect3DTexture9* destTexture = (IDirect3DTexture9*)texture->GetGPUObject();
+    IDirect3DSurface9* destSurface = 0;
+    HRESULT hr = destTexture->GetSurfaceLevel(0, &destSurface);
+    if (FAILED(hr))
+    {
+        URHO3D_LOGD3DERROR("Failed to get destination surface for resolve", hr);
+        URHO3D_SAFE_RELEASE(destSurface);
+        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;
+    }
+    else
+        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);
+            URHO3D_SAFE_RELEASE(destSurface);
+            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)
 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
 {
 {
     if (!vertexCount)
     if (!vertexCount)
@@ -1369,11 +1454,22 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
     if (index >= MAX_TEXTURE_UNITS)
     if (index >= MAX_TEXTURE_UNITS)
         return;
         return;
 
 
-    // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
     if (texture)
     if (texture)
     {
     {
+        // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
             texture = texture->GetBackupTexture();
             texture = texture->GetBackupTexture();
+        else
+        {
+            // Resolve multisampled texture now as necessary
+            if (texture->GetMultiSample() > 1 && texture->GetAutoResolve() && texture->IsResolveDirty())
+            {
+                if (texture->GetType() == Texture2D::GetTypeStatic())
+                    ResolveToTexture(static_cast<Texture2D*>(texture));
+                else if (texture->GetType() == TextureCube::GetTypeStatic())
+                    ResolveToTexture(static_cast<TextureCube*>(texture));
+            }
+        }
     }
     }
 
 
     if (texture != textures_[index])
     if (texture != textures_[index])
@@ -1518,16 +1614,23 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
         }
         }
     }
     }
 
 
-    // If the rendertarget is also bound as a texture, replace with backup texture or null
     if (renderTarget)
     if (renderTarget)
     {
     {
         Texture* parentTexture = renderTarget->GetParentTexture();
         Texture* parentTexture = renderTarget->GetParentTexture();
 
 
+        // If the rendertarget is also bound as a texture, replace with backup texture or null
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
         {
         {
             if (textures_[i] == parentTexture)
             if (textures_[i] == parentTexture)
                 SetTexture(i, textures_[i]->GetBackupTexture());
                 SetTexture(i, textures_[i]->GetBackupTexture());
         }
         }
+
+        // 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
     // First rendertarget controls sRGB write mode
@@ -1879,10 +1982,9 @@ PODVector<int> Graphics::GetMultiSampleLevels() const
     SDL_GetDesktopDisplayMode(0, &mode);
     SDL_GetDesktopDisplayMode(0, &mode);
     D3DFORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8;
     D3DFORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8;
 
 
-    for (unsigned i = (int)D3DMULTISAMPLE_2_SAMPLES; i < (int)D3DMULTISAMPLE_16_SAMPLES; ++i)
+    for (int i = (int)D3DMULTISAMPLE_2_SAMPLES; i < (int)D3DMULTISAMPLE_16_SAMPLES; ++i)
     {
     {
-        if (SUCCEEDED(impl_->interface_->CheckDeviceMultiSampleType(impl_->adapter_, impl_->deviceType_, fullscreenFormat, FALSE,
-            (D3DMULTISAMPLE_TYPE)i, NULL)))
+        if (impl_->CheckMultiSampleSupport(fullscreenFormat, i))
             ret.Push(i);
             ret.Push(i);
     }
     }
 
 

+ 6 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9GraphicsImpl.cpp

@@ -51,4 +51,10 @@ bool GraphicsImpl::CheckFormatSupport(D3DFORMAT format, DWORD usage, D3DRESOURCE
         false;
         false;
 }
 }
 
 
+bool GraphicsImpl::CheckMultiSampleSupport(D3DFORMAT format, int level)
+{
+    return interface_ ? SUCCEEDED(interface_->CheckDeviceMultiSampleType(adapter_, deviceType_, format, FALSE,
+        (D3DMULTISAMPLE_TYPE)level, NULL)) : false;
+}
+
 }
 }

+ 3 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9GraphicsImpl.h

@@ -59,6 +59,9 @@ public:
     /// Return whether a texture format and usage is supported.
     /// Return whether a texture format and usage is supported.
     bool CheckFormatSupport(D3DFORMAT format, DWORD usage, D3DRESOURCETYPE type);
     bool CheckFormatSupport(D3DFORMAT format, DWORD usage, D3DRESOURCETYPE type);
 
 
+    /// Return whether a multisample level is supported.
+    bool CheckMultiSampleSupport(D3DFORMAT format, int level);
+
 private:
 private:
     /// Direct3D interface.
     /// Direct3D interface.
     IDirect3D9* interface_;
     IDirect3D9* interface_;

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

@@ -38,7 +38,8 @@ RenderSurface::RenderSurface(Texture* parentTexture) :
     parentTexture_(parentTexture),
     parentTexture_(parentTexture),
     surface_(0),
     surface_(0),
     updateMode_(SURFACE_UPDATEVISIBLE),
     updateMode_(SURFACE_UPDATEVISIBLE),
-    updateQueued_(false)
+    updateQueued_(false),
+    resolveDirty_(false)
 {
 {
 }
 }
 
 
@@ -60,7 +61,7 @@ void RenderSurface::Release()
     URHO3D_SAFE_RELEASE(surface_);
     URHO3D_SAFE_RELEASE(surface_);
 }
 }
 
 
-bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format)
+bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format, int multiSample)
 {
 {
     // Not used on Direct3D
     // Not used on Direct3D
     return false;
     return false;

+ 52 - 9
Source/Urho3D/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -376,22 +376,22 @@ bool Texture2D::GetData(unsigned level, void* dest) const
             D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
             D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(offscreenSurface);
             URHO3D_LOGD3DERROR("Could not create surface for getting rendertarget data", hr);
             URHO3D_LOGD3DERROR("Could not create surface for getting rendertarget data", hr);
+            URHO3D_SAFE_RELEASE(offscreenSurface);
             return false;
             return false;
         }
         }
         hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface);
         hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
             URHO3D_LOGD3DERROR("Could not get rendertarget data", hr);
             URHO3D_LOGD3DERROR("Could not get rendertarget data", hr);
-            offscreenSurface->Release();
+            URHO3D_SAFE_RELEASE(offscreenSurface);
             return false;
             return false;
         }
         }
         hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY);
         hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
             URHO3D_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr);
             URHO3D_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr);
-            offscreenSurface->Release();
+            URHO3D_SAFE_RELEASE(offscreenSurface);
             return false;
             return false;
         }
         }
     }
     }
@@ -461,7 +461,7 @@ bool Texture2D::GetData(unsigned level, void* dest) const
     if (offscreenSurface)
     if (offscreenSurface)
     {
     {
         offscreenSurface->UnlockRect();
         offscreenSurface->UnlockRect();
-        offscreenSurface->Release();
+        URHO3D_SAFE_RELEASE(offscreenSurface);
     }
     }
     else
     else
         ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level);
         ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level);
@@ -482,6 +482,12 @@ bool Texture2D::Create()
         return true;
         return true;
     }
     }
 
 
+    if (multiSample_ > 1 && !autoResolve_)
+    {
+        URHO3D_LOGWARNING("Multisampled texture without autoresolve is not supported on Direct3D9");
+        autoResolve_ = true;
+    }
+
     unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
     unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
     unsigned d3dUsage = 0;
     unsigned d3dUsage = 0;
 
 
@@ -500,23 +506,36 @@ bool Texture2D::Create()
         break;
         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;
+        }
+    }
+
     IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
     IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
     // If creating a depth-stencil texture, and it is not supported, create a depth-stencil surface instead
     // If creating a depth-stencil texture, and it is not supported, create a depth-stencil surface instead
-    if (usage_ == TEXTURE_DEPTHSTENCIL && !graphics_->GetImpl()->CheckFormatSupport((D3DFORMAT)format_, d3dUsage, D3DRTYPE_TEXTURE))
+    // Multisampled surfaces need also to be created this way
+    if (usage_ == TEXTURE_DEPTHSTENCIL && (multiSample_ > 1 || !graphics_->GetImpl()->CheckFormatSupport((D3DFORMAT)format_, 
+        d3dUsage, D3DRTYPE_TEXTURE)))
     {
     {
         HRESULT hr = device->CreateDepthStencilSurface(
         HRESULT hr = device->CreateDepthStencilSurface(
             (UINT)width_,
             (UINT)width_,
             (UINT)height_,
             (UINT)height_,
             (D3DFORMAT)format_,
             (D3DFORMAT)format_,
-            D3DMULTISAMPLE_NONE,
+            (multiSample_ > 1) ? (D3DMULTISAMPLE_TYPE)multiSample_ : D3DMULTISAMPLE_NONE,
             0,
             0,
             FALSE,
             FALSE,
             (IDirect3DSurface9**)&renderSurface_->surface_,
             (IDirect3DSurface9**)&renderSurface_->surface_,
             0);
             0);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(renderSurface_->surface_);
             URHO3D_LOGD3DERROR("Could not create depth-stencil surface", hr);
             URHO3D_LOGD3DERROR("Could not create depth-stencil surface", hr);
+            URHO3D_SAFE_RELEASE(renderSurface_->surface_);
             return false;
             return false;
         }
         }
 
 
@@ -535,18 +554,42 @@ bool Texture2D::Create()
             0);
             0);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(object_.ptr_);
             URHO3D_LOGD3DERROR("Could not create texture", hr);
             URHO3D_LOGD3DERROR("Could not create texture", hr);
+            URHO3D_SAFE_RELEASE(object_.ptr_);
             return false;
             return false;
         }
         }
 
 
         levels_ = ((IDirect3DTexture9*)object_.ptr_)->GetLevelCount();
         levels_ = ((IDirect3DTexture9*)object_.ptr_)->GetLevelCount();
 
 
-        if (usage_ >= TEXTURE_RENDERTARGET)
+        // Create the multisampled rendertarget for rendering to if necessary
+        if (usage_ == TEXTURE_RENDERTARGET && multiSample_ > 1)
         {
         {
+            HRESULT hr = device->CreateRenderTarget(
+                (UINT)width_,
+                (UINT)height_,
+                (D3DFORMAT)format_,
+                (D3DMULTISAMPLE_TYPE)multiSample_,
+                0,
+                FALSE,
+                (IDirect3DSurface9**)&renderSurface_->surface_,
+                0);
+            if (FAILED(hr))
+            {
+                URHO3D_LOGD3DERROR("Could not create multisampled rendertarget surface", hr);
+                URHO3D_SAFE_RELEASE(renderSurface_->surface_);
+                return false;
+            }
+        }
+        else if (usage_ >= TEXTURE_RENDERTARGET)
+        {
+            // Else use the texture surface directly for rendering
             hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&renderSurface_->surface_);
             hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&renderSurface_->surface_);
             if (FAILED(hr))
             if (FAILED(hr))
+            {
                 URHO3D_LOGD3DERROR("Could not get rendertarget surface", hr);
                 URHO3D_LOGD3DERROR("Could not get rendertarget surface", hr);
+                URHO3D_SAFE_RELEASE(renderSurface_->surface_);
+                return false;
+            }
         }
         }
     }
     }
 
 

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

@@ -485,8 +485,8 @@ bool Texture3D::Create()
         0);
         0);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(object_.ptr_);
         URHO3D_LOGD3DERROR("Could not create texture", hr);
         URHO3D_LOGD3DERROR("Could not create texture", hr);
+        URHO3D_SAFE_RELEASE(object_.ptr_);
         return false;
         return false;
     }
     }
 
 

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

@@ -443,21 +443,21 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
         HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
         HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
-            URHO3D_SAFE_RELEASE(offscreenSurface);
             URHO3D_LOGD3DERROR("Could not create surface for getting rendertarget data", hr);
             URHO3D_LOGD3DERROR("Could not create surface for getting rendertarget data", hr);
+            URHO3D_SAFE_RELEASE(offscreenSurface);
             return false;
             return false;
         }
         }
         hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurfaces_[face]->GetSurface(), offscreenSurface);
         hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurfaces_[face]->GetSurface(), offscreenSurface);
         if (FAILED(hr))
         if (FAILED(hr))
         {
         {
             URHO3D_LOGD3DERROR("Could not get rendertarget data", hr);
             URHO3D_LOGD3DERROR("Could not get rendertarget data", hr);
-            offscreenSurface->Release();
+            URHO3D_SAFE_RELEASE(offscreenSurface);
             return false;
             return false;
         }
         }
         if (FAILED(offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
         if (FAILED(offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
         {
         {
             URHO3D_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr);
             URHO3D_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr);
-            offscreenSurface->Release();
+            URHO3D_SAFE_RELEASE(offscreenSurface);
             return false;
             return false;
         }
         }
     }
     }
@@ -527,7 +527,7 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
     if (offscreenSurface)
     if (offscreenSurface)
     {
     {
         offscreenSurface->UnlockRect();
         offscreenSurface->UnlockRect();
-        offscreenSurface->Release();
+        URHO3D_SAFE_RELEASE(offscreenSurface);
     }
     }
     else
     else
         ((IDirect3DCubeTexture9*)object_.ptr_)->UnlockRect((D3DCUBEMAP_FACES)face, level);
         ((IDirect3DCubeTexture9*)object_.ptr_)->UnlockRect((D3DCUBEMAP_FACES)face, level);
@@ -563,6 +563,17 @@ bool TextureCube::Create()
         break;
         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;
+        }
+    }
+
     IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
     IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
     HRESULT hr = device->CreateCubeTexture(
     HRESULT hr = device->CreateCubeTexture(
         (UINT)width_,
         (UINT)width_,
@@ -574,8 +585,8 @@ bool TextureCube::Create()
         0);
         0);
     if (FAILED(hr))
     if (FAILED(hr))
     {
     {
-        URHO3D_SAFE_RELEASE(object_.ptr_);
         URHO3D_LOGD3DERROR("Could not create cube texture", hr);
         URHO3D_LOGD3DERROR("Could not create cube texture", hr);
+        URHO3D_SAFE_RELEASE(object_.ptr_);
         return false;
         return false;
     }
     }
 
 
@@ -585,10 +596,36 @@ bool TextureCube::Create()
     {
     {
         for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
         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_LOGD3DERROR("Could not create multisampled rendertarget surface", hr);
+                    URHO3D_SAFE_RELEASE(renderSurfaces_[i]->surface_);
+                    return false;
+                }
+            }
+            else
+            {
+                hr = ((IDirect3DCubeTexture9*)object_.ptr_)->GetCubeMapSurface((D3DCUBEMAP_FACES)i, 0,
+                    (IDirect3DSurface9**)&renderSurfaces_[i]->surface_);
+                if (FAILED(hr))
+                {
+                    URHO3D_LOGD3DERROR("Could not get rendertarget surface", hr);
+                    URHO3D_SAFE_RELEASE(renderSurfaces_[i]->surface_);
+                    return false;
+                }
+            }
         }
         }
     }
     }
 
 

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

@@ -128,6 +128,10 @@ public:
     void Clear(unsigned flags, const Color& color = Color(0.0f, 0.0f, 0.0f, 0.0f), float depth = 1.0f, unsigned stencil = 0);
     void Clear(unsigned flags, const Color& color = Color(0.0f, 0.0f, 0.0f, 0.0f), float depth = 1.0f, unsigned stencil = 0);
     /// Resolve multisampled backbuffer to a texture rendertarget. The texture's size should match the viewport size.
     /// Resolve multisampled backbuffer to a texture rendertarget. The texture's size should match the viewport size.
     bool ResolveToTexture(Texture2D* destination, const IntRect& viewport);
     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.
     /// Draw non-indexed geometry.
     void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
     void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
     /// Draw indexed geometry.
     /// Draw indexed geometry.
@@ -584,7 +588,7 @@ private:
     /// Bind a framebuffer using either extension or core functionality. Used only on OpenGL.
     /// Bind a framebuffer using either extension or core functionality. Used only on OpenGL.
     void BindFramebuffer(unsigned fbo);
     void BindFramebuffer(unsigned fbo);
     /// Bind a framebuffer color attachment using either extension or core functionality. Used only on OpenGL.
     /// Bind a framebuffer color attachment using either extension or core functionality. Used only on OpenGL.
-    void BindColorAttachment(unsigned index, unsigned target, unsigned object);
+    void BindColorAttachment(unsigned index, unsigned target, unsigned object, bool isRenderBuffer);
     /// Bind a framebuffer depth attachment using either extension or core functionality. Used only on OpenGL.
     /// Bind a framebuffer depth attachment using either extension or core functionality. Used only on OpenGL.
     void BindDepthAttachment(unsigned object, bool isRenderBuffer);
     void BindDepthAttachment(unsigned object, bool isRenderBuffer);
     /// Bind a framebuffer stencil attachment using either extension or core functionality. Used only on OpenGL.
     /// Bind a framebuffer stencil attachment using either extension or core functionality. Used only on OpenGL.

+ 195 - 16
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -37,6 +37,7 @@
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderProgram.h"
 #include "../../Graphics/ShaderVariation.h"
 #include "../../Graphics/ShaderVariation.h"
 #include "../../Graphics/Texture2D.h"
 #include "../../Graphics/Texture2D.h"
+#include "../../Graphics/TextureCube.h"
 #include "../../Graphics/VertexBuffer.h"
 #include "../../Graphics/VertexBuffer.h"
 #include "../../IO/File.h"
 #include "../../IO/File.h"
 #include "../../IO/Log.h"
 #include "../../IO/Log.h"
@@ -748,6 +749,130 @@ bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
     return true;
     return true;
 }
 }
 
 
+bool Graphics::ResolveToTexture(Texture2D* texture)
+{
+#ifndef GL_ES_VERSION_2_0
+    if (!texture)
+        return false;
+    RenderSurface* surface = texture->GetRenderSurface();
+    if (!surface || !surface->GetRenderBuffer())
+        return false;
+
+    URHO3D_PROFILE(ResolveToTexture);
+
+    texture->SetResolveDirty(false);
+    surface->SetResolveDirty(false);
+
+    // Use separate FBOs for resolve to not disturb the currently set rendertarget(s)
+    if (!impl_->resolveSrcFBO_)
+        impl_->resolveSrcFBO_ = CreateFramebuffer();
+    if (!impl_->resolveDestFBO_)
+        impl_->resolveDestFBO_ = CreateFramebuffer();
+
+    if (!gl3Support)
+    {
+        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, impl_->resolveSrcFBO_);
+        glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT,
+            surface->GetRenderBuffer());
+        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, impl_->resolveDestFBO_);
+        glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture->GetGPUObjectName(),
+            0);
+        glBlitFramebufferEXT(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
+            GL_COLOR_BUFFER_BIT, GL_NEAREST);
+        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+    }
+    else
+    {
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, impl_->resolveSrcFBO_);
+        glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->GetRenderBuffer());
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, impl_->resolveDestFBO_);
+        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->GetGPUObjectName(), 0);
+        glBlitFramebuffer(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(), 
+            GL_COLOR_BUFFER_BIT, GL_NEAREST);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    }
+
+    // Restore previously bound FBO
+    BindFramebuffer(impl_->boundFBO_);
+    return true;
+#else
+    // Not supported on GLES
+    return false;
+#endif
+}
+
+bool Graphics::ResolveToTexture(TextureCube* texture)
+{
+#ifndef GL_ES_VERSION_2_0
+    if (!texture)
+        return false;
+
+    URHO3D_PROFILE(ResolveToTexture);
+
+    texture->SetResolveDirty(false);
+
+    // Use separate FBOs for resolve to not disturb the currently set rendertarget(s)
+    if (!impl_->resolveSrcFBO_)
+        impl_->resolveSrcFBO_ = CreateFramebuffer();
+    if (!impl_->resolveDestFBO_)
+        impl_->resolveDestFBO_ = CreateFramebuffer();
+
+    if (!gl3Support)
+    {
+        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);
+            glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, impl_->resolveSrcFBO_);
+            glFramebufferRenderbufferEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT,
+                surface->GetRenderBuffer());
+            glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, impl_->resolveDestFBO_);
+            glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
+                texture->GetGPUObjectName(), 0);
+            glBlitFramebufferEXT(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
+                GL_COLOR_BUFFER_BIT, GL_NEAREST);
+        }
+
+        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+    }
+    else
+    {
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        {
+            RenderSurface* surface = texture->GetRenderSurface((CubeMapFace)i);
+            if (!surface->IsResolveDirty())
+                continue;
+            
+            surface->SetResolveDirty(false);
+            glBindFramebuffer(GL_READ_FRAMEBUFFER, impl_->resolveSrcFBO_);
+            glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->GetRenderBuffer());
+            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, impl_->resolveDestFBO_);
+            glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
+                texture->GetGPUObjectName(), 0);
+            glBlitFramebuffer(0, 0, texture->GetWidth(), texture->GetHeight(), 0, 0, texture->GetWidth(), texture->GetHeight(),
+                GL_COLOR_BUFFER_BIT, GL_NEAREST);
+        }
+
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    }
+
+    // Restore previously bound FBO
+    BindFramebuffer(impl_->boundFBO_);
+    return true;
+#else
+    // Not supported on GLES
+    return false;
+#endif
+}
+
 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
 void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
 {
 {
     if (!vertexCount)
     if (!vertexCount)
@@ -1127,7 +1252,6 @@ void Graphics::SetShaderParameter(StringHash param, float value)
 void Graphics::SetShaderParameter(StringHash param, bool value)
 void Graphics::SetShaderParameter(StringHash param, bool value)
 {
 {
     // \todo Not tested
     // \todo Not tested
-
     if (impl_->shaderProgram_)
     if (impl_->shaderProgram_)
     {
     {
         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
         const ShaderParameter* info = impl_->shaderProgram_->GetParameter(param);
@@ -1441,6 +1565,17 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
     {
     {
         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
         if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
             texture = texture->GetBackupTexture();
             texture = texture->GetBackupTexture();
+        else
+        {
+            // Resolve multisampled texture now as necessary
+            if (texture->GetMultiSample() > 1 && texture->GetAutoResolve() && texture->IsResolveDirty())
+            {
+                if (texture->GetType() == Texture2D::GetTypeStatic())
+                    ResolveToTexture(static_cast<Texture2D*>(texture));
+                if (texture->GetType() == TextureCube::GetTypeStatic())
+                    ResolveToTexture(static_cast<TextureCube*>(texture));
+            }
+        }
     }
     }
 
 
     if (textures_[index] != texture)
     if (textures_[index] != texture)
@@ -1573,6 +1708,13 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
                 if (textures_[i] == parentTexture)
                 if (textures_[i] == parentTexture)
                     SetTexture(i, textures_[i]->GetBackupTexture());
                     SetTexture(i, textures_[i]->GetBackupTexture());
             }
             }
+
+            // If multisampled, mark the texture & surface needing resolve
+            if (parentTexture->GetMultiSample() > 1 && parentTexture->GetAutoResolve())
+            {
+                parentTexture->SetResolveDirty(true);
+                renderTarget->SetResolveDirty(true);
+            }
         }
         }
 
 
         impl_->fboDirty_ = true;
         impl_->fboDirty_ = true;
@@ -1592,7 +1734,9 @@ void Graphics::SetDepthStencil(RenderSurface* depthStencil)
 {
 {
     // If we are using a rendertarget texture, it is required in OpenGL to also have an own depth-stencil
     // If we are using a rendertarget texture, it is required in OpenGL to also have an own depth-stencil
     // Create a new depth-stencil texture as necessary to be able to provide similar behaviour as Direct3D9
     // Create a new depth-stencil texture as necessary to be able to provide similar behaviour as Direct3D9
-    if (renderTargets_[0] && !depthStencil)
+    // Only do this for non-multisampled rendertargets; when using multisampled target a similarly multisampled
+    // depth-stencil should also be provided (backbuffer depth isn't compatible)
+    if (renderTargets_[0] && renderTargets_[0]->GetMultiSample() == 1 && !depthStencil)
     {
     {
         int width = renderTargets_[0]->GetWidth();
         int width = renderTargets_[0]->GetWidth();
         int height = renderTargets_[0]->GetHeight();
         int height = renderTargets_[0]->GetHeight();
@@ -2171,7 +2315,7 @@ void Graphics::CleanupRenderSurface(RenderSurface* surface)
                     BindFramebuffer(i->second_.fbo_);
                     BindFramebuffer(i->second_.fbo_);
                     currentFBO = i->second_.fbo_;
                     currentFBO = i->second_.fbo_;
                 }
                 }
-                BindColorAttachment(j, GL_TEXTURE_2D, 0);
+                BindColorAttachment(j, GL_TEXTURE_2D, 0, false);
                 i->second_.colorAttachments_[j] = 0;
                 i->second_.colorAttachments_[j] = 0;
                 // Mark drawbuffer bits to need recalculation
                 // Mark drawbuffer bits to need recalculation
                 i->second_.drawBuffers_ = M_MAX_UNSIGNED;
                 i->second_.drawBuffers_ = M_MAX_UNSIGNED;
@@ -2827,25 +2971,38 @@ void Graphics::PrepareDraw()
             {
             {
                 Texture* texture = renderTargets_[j]->GetParentTexture();
                 Texture* texture = renderTargets_[j]->GetParentTexture();
 
 
-                // If texture's parameters are dirty, update before attaching
-                if (texture->GetParametersDirty())
+                // Bind either a renderbuffer or texture, depending on what is available
+                unsigned renderBufferID = renderTargets_[j]->GetRenderBuffer();
+                if (!renderBufferID)
                 {
                 {
-                    SetTextureForUpdate(texture);
-                    texture->UpdateParameters();
-                    SetTexture(0, 0);
-                }
+                    // If texture's parameters are dirty, update before attaching
+                    if (texture->GetParametersDirty())
+                    {
+                        SetTextureForUpdate(texture);
+                        texture->UpdateParameters();
+                        SetTexture(0, 0);
+                    }
 
 
-                if (i->second_.colorAttachments_[j] != renderTargets_[j])
+                    if (i->second_.colorAttachments_[j] != renderTargets_[j])
+                    {
+                        BindColorAttachment(j, renderTargets_[j]->GetTarget(), texture->GetGPUObjectName(), false);
+                        i->second_.colorAttachments_[j] = renderTargets_[j];
+                    }
+                }
+                else
                 {
                 {
-                    BindColorAttachment(j, renderTargets_[j]->GetTarget(), texture->GetGPUObjectName());
-                    i->second_.colorAttachments_[j] = renderTargets_[j];
+                    if (i->second_.colorAttachments_[j] != renderTargets_[j])
+                    {
+                        BindColorAttachment(j, renderTargets_[j]->GetTarget(), renderBufferID, true);
+                        i->second_.colorAttachments_[j] = renderTargets_[j];
+                    }
                 }
                 }
             }
             }
             else
             else
             {
             {
                 if (i->second_.colorAttachments_[j])
                 if (i->second_.colorAttachments_[j])
                 {
                 {
-                    BindColorAttachment(j, GL_TEXTURE_2D, 0);
+                    BindColorAttachment(j, GL_TEXTURE_2D, 0, false);
                     i->second_.colorAttachments_[j] = 0;
                     i->second_.colorAttachments_[j] = 0;
                 }
                 }
             }
             }
@@ -3009,9 +3166,18 @@ void Graphics::CleanupFramebuffers()
         for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
         for (HashMap<unsigned long long, FrameBufferObject>::Iterator i = impl_->frameBuffers_.Begin();
              i != impl_->frameBuffers_.End(); ++i)
              i != impl_->frameBuffers_.End(); ++i)
             DeleteFramebuffer(i->second_.fbo_);
             DeleteFramebuffer(i->second_.fbo_);
+
+        if (impl_->resolveSrcFBO_)
+            DeleteFramebuffer(impl_->resolveSrcFBO_);
+        if (impl_->resolveDestFBO_)
+            DeleteFramebuffer(impl_->resolveDestFBO_);
     }
     }
     else
     else
+    {
         impl_->boundFBO_ = 0;
         impl_->boundFBO_ = 0;
+        impl_->resolveSrcFBO_ = 0;
+        impl_->resolveDestFBO_ = 0;
+    }
 
 
     impl_->frameBuffers_.Clear();
     impl_->frameBuffers_.Clear();
 }
 }
@@ -3138,14 +3304,27 @@ void Graphics::BindFramebuffer(unsigned fbo)
         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
 }
 }
 
 
-void Graphics::BindColorAttachment(unsigned index, unsigned target, unsigned object)
+void Graphics::BindColorAttachment(unsigned index, unsigned target, unsigned object, bool isRenderBuffer)
 {
 {
+    if (!object)
+        isRenderBuffer = false;
+
 #ifndef GL_ES_VERSION_2_0
 #ifndef GL_ES_VERSION_2_0
     if (!gl3Support)
     if (!gl3Support)
-        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, object, 0);
+    {
+        if (!isRenderBuffer)
+            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, object, 0);
+        else
+            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_RENDERBUFFER_EXT, object);
+    }
     else
     else
 #endif
 #endif
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, target, object, 0);
+    {
+        if (!isRenderBuffer)
+            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, target, object, 0);
+        else
+            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_RENDERBUFFER, object);
+    }
 }
 }
 
 
 void Graphics::BindDepthAttachment(unsigned object, bool isRenderBuffer)
 void Graphics::BindDepthAttachment(unsigned object, bool isRenderBuffer)

+ 2 - 0
Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.cpp

@@ -41,6 +41,8 @@ GraphicsImpl::GraphicsImpl() :
     boundFBO_(0),
     boundFBO_(0),
     boundVBO_(0),
     boundVBO_(0),
     boundUBO_(0),
     boundUBO_(0),
+    resolveSrcFBO_(0),
+    resolveDestFBO_(0),
     pixelFormat_(0),
     pixelFormat_(0),
     fboDirty_(false),
     fboDirty_(false),
     vertexBuffersDirty_(false),
     vertexBuffersDirty_(false),

+ 4 - 0
Source/Urho3D/Graphics/OpenGL/OGLGraphicsImpl.h

@@ -132,6 +132,10 @@ private:
     unsigned boundVBO_;
     unsigned boundVBO_;
     /// Currently bound uniform buffer object.
     /// Currently bound uniform buffer object.
     unsigned boundUBO_;
     unsigned boundUBO_;
+    /// Read frame buffer for multisampled texture resolves.
+    unsigned resolveSrcFBO_;
+    /// Write frame buffer for multisampled texture resolves.
+    unsigned resolveDestFBO_;
     /// Current pixel format.
     /// Current pixel format.
     int pixelFormat_;
     int pixelFormat_;
     /// Map for FBO's per resolution and format.
     /// Map for FBO's per resolution and format.

+ 26 - 5
Source/Urho3D/Graphics/OpenGL/OGLRenderSurface.cpp

@@ -51,7 +51,7 @@ RenderSurface::RenderSurface(Texture* parentTexture) :
 {
 {
 }
 }
 
 
-bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format)
+bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format, int multiSample)
 {
 {
     Graphics* graphics = parentTexture_->GetGraphics();
     Graphics* graphics = parentTexture_->GetGraphics();
     if (!graphics)
     if (!graphics)
@@ -59,10 +59,31 @@ bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned
 
 
     Release();
     Release();
 
 
-    glGenRenderbuffersEXT(1, &renderBuffer_);
-    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderBuffer_);
-    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, width, height);
-    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+#ifndef GL_ES_VERSION_2_0
+    if (Graphics::GetGL3Support())
+    {
+        glGenRenderbuffers(1, &renderBuffer_);
+        glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer_);
+        if (multiSample > 1)
+            glRenderbufferStorageMultisample(GL_RENDERBUFFER, multiSample, format, width, height);
+        else
+            glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
+        glBindRenderbuffer(GL_RENDERBUFFER, 0);
+    }
+    else
+#endif
+    {
+        glGenRenderbuffersEXT(1, &renderBuffer_);
+        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderBuffer_);
+#ifndef GL_ES_VERSION_2_0
+        if (multiSample > 1)
+            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multiSample, format, width, height);
+        else
+#endif
+            glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, width, height);
+        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+    }
+
     return true;
     return true;
 }
 }
 
 

+ 9 - 0
Source/Urho3D/Graphics/OpenGL/OGLTexture.cpp

@@ -89,6 +89,15 @@ void Texture::UpdateParameters()
     if (!object_.name_ || !graphics_)
     if (!object_.name_ || !graphics_)
         return;
         return;
 
 
+    // If texture is multisampled, do not attempt to set parameters as it's illegal, just return
+#ifndef GL_ES_VERSION_2_0
+    if (target_ == GL_TEXTURE_2D_MULTISAMPLE)
+    {
+        parametersDirty_ = false;
+        return;
+    }
+#endif
+
     // Wrapping
     // Wrapping
     glTexParameteri(target_, GL_TEXTURE_WRAP_S, GetWrapMode(addressMode_[COORD_U]));
     glTexParameteri(target_, GL_TEXTURE_WRAP_S, GetWrapMode(addressMode_[COORD_U]));
     glTexParameteri(target_, GL_TEXTURE_WRAP_T, GetWrapMode(addressMode_[COORD_V]));
     glTexParameteri(target_, GL_TEXTURE_WRAP_T, GetWrapMode(addressMode_[COORD_V]));

+ 41 - 1
Source/Urho3D/Graphics/OpenGL/OGLTexture2D.cpp

@@ -362,6 +362,15 @@ bool Texture2D::Create()
         return true;
         return true;
     }
     }
 
 
+#ifdef GL_ES_VERSION_2_0
+    if (multiSample_ > 1)
+    {
+        URHO3D_LOGWARNING("Multisampled texture is not supported on OpenGL ES");
+        multiSample_ = 1;
+        autoResolve_ = false;
+    }
+#endif
+
     unsigned format = GetSRGB() ? GetSRGBFormat(format_) : format_;
     unsigned format = GetSRGB() ? GetSRGBFormat(format_) : format_;
     unsigned externalFormat = GetExternalFormat(format_);
     unsigned externalFormat = GetExternalFormat(format_);
     unsigned dataType = GetDataType(format_);
     unsigned dataType = GetDataType(format_);
@@ -377,12 +386,38 @@ bool Texture2D::Create()
     {
     {
         if (renderSurface_)
         if (renderSurface_)
         {
         {
-            renderSurface_->CreateRenderBuffer(width_, height_, format);
+            renderSurface_->CreateRenderBuffer(width_, height_, format, multiSample_);
             return true;
             return true;
         }
         }
         else
         else
             return false;
             return false;
     }
     }
+    else
+    {
+        if (multiSample_ > 1)
+        {
+            if (autoResolve_)
+            {
+                // Multisample with autoresolve: create a renderbuffer for rendering, but also a texture
+                renderSurface_->CreateRenderBuffer(width_, height_, format, multiSample_);
+            }
+            else
+            {
+                // Multisample without autoresolve: create a texture only
+#ifndef GL_ES_VERSION_2_0
+                if (!Graphics::GetGL3Support() && !GLEW_ARB_texture_multisample)
+                {
+                    URHO3D_LOGERROR("Multisampled texture extension not available");
+                    return false;
+                }
+
+                target_ = GL_TEXTURE_2D_MULTISAMPLE;
+                if (renderSurface_)
+                    renderSurface_->target_ = GL_TEXTURE_2D_MULTISAMPLE;
+#endif
+            }
+        }
+    }
 
 
     glGenTextures(1, &object_.name_);
     glGenTextures(1, &object_.name_);
 
 
@@ -395,6 +430,11 @@ bool Texture2D::Create()
     if (!IsCompressed())
     if (!IsCompressed())
     {
     {
         glGetError();
         glGetError();
+#ifndef GL_ES_VERSION_2_0
+        if (multiSample_ > 1 && !autoResolve_)
+            glTexImage2DMultisample(target_, multiSample_, format, width_, height_, GL_TRUE);
+        else
+#endif
         glTexImage2D(target_, 0, format, width_, height_, 0, externalFormat, dataType, 0);
         glTexImage2D(target_, 0, format, width_, height_, 0, externalFormat, dataType, 0);
         if (glGetError())
         if (glGetError())
         {
         {

+ 16 - 0
Source/Urho3D/Graphics/OpenGL/OGLTextureCube.cpp

@@ -428,6 +428,15 @@ bool TextureCube::Create()
         return true;
         return true;
     }
     }
 
 
+#ifdef GL_ES_VERSION_2_0
+    if (multiSample_ > 1)
+    {
+        URHO3D_LOGWARNING("Multisampled texture is not supported on OpenGL ES");
+        multiSample_ = 1;
+        autoResolve_ = false;
+    }
+#endif
+    
     glGenTextures(1, &object_.name_);
     glGenTextures(1, &object_.name_);
 
 
     // Ensure that our texture is bound to OpenGL texture unit 0
     // Ensure that our texture is bound to OpenGL texture unit 0
@@ -438,6 +447,13 @@ bool TextureCube::Create()
     unsigned externalFormat = GetExternalFormat(format_);
     unsigned externalFormat = GetExternalFormat(format_);
     unsigned dataType = GetDataType(format_);
     unsigned dataType = GetDataType(format_);
 
 
+    // If multisample, create renderbuffers for each face
+    if (multiSample_ > 1)
+    {
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+            renderSurfaces_[i]->CreateRenderBuffer(width_, height_, format, multiSample_);
+    }
+
     bool success = true;
     bool success = true;
     if (!IsCompressed())
     if (!IsCompressed())
     {
     {

+ 5 - 0
Source/Urho3D/Graphics/RenderPath.cpp

@@ -106,6 +106,11 @@ void RenderTargetInfo::Load(const XMLElement& element)
         size_.x_ = element.GetFloat("width");
         size_.x_ = element.GetFloat("width");
     if (element.HasAttribute("height"))
     if (element.HasAttribute("height"))
         size_.y_ = element.GetFloat("height");
         size_.y_ = element.GetFloat("height");
+
+    if (element.HasAttribute("multisample"))
+        multiSample_ = Clamp(element.GetInt("multisample"), 1, 16);
+    if (element.HasAttribute("autoresolve"))
+        autoResolve_ = element.GetBool("autoresolve");
 }
 }
 
 
 void RenderPathCommand::Load(const XMLElement& element)
 void RenderPathCommand::Load(const XMLElement& element)

+ 6 - 0
Source/Urho3D/Graphics/RenderPath.h

@@ -69,6 +69,8 @@ struct URHO3D_API RenderTargetInfo
     RenderTargetInfo() :
     RenderTargetInfo() :
         size_(Vector2::ZERO),
         size_(Vector2::ZERO),
         sizeMode_(SIZE_ABSOLUTE),
         sizeMode_(SIZE_ABSOLUTE),
+        multiSample_(1),
+        autoResolve_(true),
         enabled_(true),
         enabled_(true),
         cubemap_(false),
         cubemap_(false),
         filtered_(false),
         filtered_(false),
@@ -90,6 +92,10 @@ struct URHO3D_API RenderTargetInfo
     Vector2 size_;
     Vector2 size_;
     /// Size mode.
     /// Size mode.
     RenderTargetSizeMode sizeMode_;
     RenderTargetSizeMode sizeMode_;
+    /// Multisampling level (1 = no multisampling).
+    int multiSample_;
+    /// Multisampling autoresolve flag.
+    bool autoResolve_;
     /// Enabled flag.
     /// Enabled flag.
     bool enabled_;
     bool enabled_;
     /// Cube map flag.
     /// Cube map flag.

+ 10 - 0
Source/Urho3D/Graphics/RenderSurface.cpp

@@ -94,6 +94,16 @@ TextureUsage RenderSurface::GetUsage() const
     return parentTexture_->GetUsage();
     return parentTexture_->GetUsage();
 }
 }
 
 
+int RenderSurface::GetMultiSample() const
+{
+    return parentTexture_->GetMultiSample();
+}
+
+bool RenderSurface::GetAutoResolve() const
+{
+    return parentTexture_->GetAutoResolve();
+}
+
 Viewport* RenderSurface::GetViewport(unsigned index) const
 Viewport* RenderSurface::GetViewport(unsigned index) const
 {
 {
     return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
     return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;

+ 15 - 1
Source/Urho3D/Graphics/RenderSurface.h

@@ -60,7 +60,7 @@ public:
     /// Mark the GPU resource destroyed on graphics context destruction. Only used on OpenGL.
     /// Mark the GPU resource destroyed on graphics context destruction. Only used on OpenGL.
     void OnDeviceLost();
     void OnDeviceLost();
     /// Create renderbuffer that cannot be sampled as a texture. Only used on OpenGL.
     /// Create renderbuffer that cannot be sampled as a texture. Only used on OpenGL.
-    bool CreateRenderBuffer(unsigned width, unsigned height, unsigned format);
+    bool CreateRenderBuffer(unsigned width, unsigned height, unsigned format, int multiSample);
 
 
     /// Return width.
     /// Return width.
     int GetWidth() const;
     int GetWidth() const;
@@ -71,6 +71,12 @@ public:
     /// Return usage.
     /// Return usage.
     TextureUsage GetUsage() const;
     TextureUsage GetUsage() const;
 
 
+    /// Return multisampling level.
+    int GetMultiSample() const;
+
+    /// Return multisampling autoresolve mode.
+    bool GetAutoResolve() const;
+
     /// Return number of viewports.
     /// Return number of viewports.
     unsigned GetNumViewports() const { return viewports_.Size(); }
     unsigned GetNumViewports() const { return viewports_.Size(); }
 
 
@@ -110,6 +116,12 @@ public:
     /// Return OpenGL renderbuffer if created.
     /// Return OpenGL renderbuffer if created.
     unsigned GetRenderBuffer() const { return renderBuffer_; }
     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:
 private:
     /// Parent texture.
     /// Parent texture.
     Texture* parentTexture_;
     Texture* parentTexture_;
@@ -142,6 +154,8 @@ private:
     RenderSurfaceUpdateMode updateMode_;
     RenderSurfaceUpdateMode updateMode_;
     /// Update queued flag.
     /// Update queued flag.
     bool updateQueued_;
     bool updateQueued_;
+    /// Multisampled resolve dirty flag.
+    bool resolveDirty_;
 };
 };
 
 
 }
 }

+ 31 - 10
Source/Urho3D/Graphics/Renderer.cpp

@@ -278,6 +278,7 @@ Renderer::Renderer(Context* context) :
     shadowQuality_(SHADOWQUALITY_PCF_16BIT),
     shadowQuality_(SHADOWQUALITY_PCF_16BIT),
     shadowSoftness_(1.0f),
     shadowSoftness_(1.0f),
     vsmShadowParams_(0.0000001f, 0.2f),
     vsmShadowParams_(0.0000001f, 0.2f),
+    vsmMultiSample_(1),
     maxShadowMaps_(1),
     maxShadowMaps_(1),
     minInstances_(2),
     minInstances_(2),
     maxSortedInstances_(1000),
     maxSortedInstances_(1000),
@@ -456,6 +457,16 @@ void Renderer::SetVSMShadowParameters(float minVariance, float lightBleedingRedu
     vsmShadowParams_.y_ = Clamp(lightBleedingReduction, 0.0f, 1.0f);
     vsmShadowParams_.y_ = Clamp(lightBleedingReduction, 0.0f, 1.0f);
 }
 }
 
 
+void Renderer::SetVSMMultiSample(int multiSample)
+{
+    multiSample = Clamp(multiSample, 1, 16);
+    if (multiSample != vsmMultiSample_)
+    {
+        vsmMultiSample_ = multiSample;
+        ResetShadowMaps();
+    }
+}
+
 void Renderer::SetShadowMapFilter(Object* instance, ShadowMapFilter functionPtr)
 void Renderer::SetShadowMapFilter(Object* instance, ShadowMapFilter functionPtr)
 {
 {
     shadowMapFilterInstance_ = instance;
     shadowMapFilterInstance_ = instance;
@@ -916,6 +927,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     // Find format and usage of the shadow map
     // Find format and usage of the shadow map
     unsigned shadowMapFormat = 0;
     unsigned shadowMapFormat = 0;
     TextureUsage shadowMapUsage = TEXTURE_DEPTHSTENCIL;
     TextureUsage shadowMapUsage = TEXTURE_DEPTHSTENCIL;
+    int multiSample = 1;
 
 
     switch (shadowQuality_)
     switch (shadowQuality_)
     {
     {
@@ -933,6 +945,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     case SHADOWQUALITY_BLUR_VSM:
     case SHADOWQUALITY_BLUR_VSM:
         shadowMapFormat = graphics_->GetRGFloat32Format();
         shadowMapFormat = graphics_->GetRGFloat32Format();
         shadowMapUsage = TEXTURE_RENDERTARGET;
         shadowMapUsage = TEXTURE_RENDERTARGET;
+        multiSample = vsmMultiSample_;
         break;
         break;
     }
     }
 
 
@@ -945,7 +958,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
 
 
     while (retries)
     while (retries)
     {
     {
-        if (!newShadowMap->SetSize(width, height, shadowMapFormat, shadowMapUsage))
+        if (!newShadowMap->SetSize(width, height, shadowMapFormat, shadowMapUsage, multiSample))
         {
         {
             width >>= 1;
             width >>= 1;
             height >>= 1;
             height >>= 1;
@@ -991,7 +1004,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     return newShadowMap;
     return newShadowMap;
 }
 }
 
 
-Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool cubemap, bool filtered, bool srgb,
+Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, int multiSample, bool autoResolve, bool cubemap, bool filtered, bool srgb,
     unsigned persistentKey)
     unsigned persistentKey)
 {
 {
     bool depthStencil = (format == Graphics::GetDepthStencilFormat()) || (format == Graphics::GetReadableDepthFormat());
     bool depthStencil = (format == Graphics::GetDepthStencilFormat()) || (format == Graphics::GetReadableDepthFormat());
@@ -1004,13 +1017,19 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool
     if (cubemap)
     if (cubemap)
         height = width;
         height = width;
 
 
-    long long searchKey = ((long long)format << 32) | (width << 16) | height;
+    multiSample = Clamp(multiSample, 1, 16);
+    if (multiSample == 1)
+        autoResolve = false;
+
+    long long searchKey = ((long long)format << 32) | (multiSample << 24) | (width << 12) | height;
     if (filtered)
     if (filtered)
         searchKey |= 0x8000000000000000LL;
         searchKey |= 0x8000000000000000LL;
     if (srgb)
     if (srgb)
         searchKey |= 0x4000000000000000LL;
         searchKey |= 0x4000000000000000LL;
     if (cubemap)
     if (cubemap)
         searchKey |= 0x2000000000000000LL;
         searchKey |= 0x2000000000000000LL;
+    if (autoResolve)
+        searchKey |= 0x1000000000000000LL;
 
 
     // Add persistent key if defined
     // Add persistent key if defined
     if (persistentKey)
     if (persistentKey)
@@ -1032,7 +1051,7 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool
         if (!cubemap)
         if (!cubemap)
         {
         {
             SharedPtr<Texture2D> newTex2D(new Texture2D(context_));
             SharedPtr<Texture2D> newTex2D(new Texture2D(context_));
-            newTex2D->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET);
+            newTex2D->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET, multiSample, autoResolve);
 
 
 #ifdef URHO3D_OPENGL
 #ifdef URHO3D_OPENGL
             // OpenGL hack: clear persistent floating point screen buffers to ensure the initial contents aren't illegal (NaN)?
             // OpenGL hack: clear persistent floating point screen buffers to ensure the initial contents aren't illegal (NaN)?
@@ -1053,7 +1072,7 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool
         else
         else
         {
         {
             SharedPtr<TextureCube> newTexCube(new TextureCube(context_));
             SharedPtr<TextureCube> newTexCube(new TextureCube(context_));
-            newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET);
+            newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET, multiSample);
 
 
             newBuffer = newTexCube;
             newBuffer = newTexCube;
         }
         }
@@ -1074,7 +1093,7 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, bool
     }
     }
 }
 }
 
 
-RenderSurface* Renderer::GetDepthStencil(int width, int height)
+RenderSurface* Renderer::GetDepthStencil(int width, int height, int multiSample, bool autoResolve)
 {
 {
     // Return the default depth-stencil surface if applicable
     // Return the default depth-stencil surface if applicable
     // (when using OpenGL Graphics will allocate right size surfaces on demand to emulate Direct3D9)
     // (when using OpenGL Graphics will allocate right size surfaces on demand to emulate Direct3D9)
@@ -1082,8 +1101,8 @@ RenderSurface* Renderer::GetDepthStencil(int width, int height)
         return 0;
         return 0;
     else
     else
     {
     {
-        return static_cast<Texture2D*>(GetScreenBuffer(width, height, Graphics::GetDepthStencilFormat(), false, false,
-            false))->GetRenderSurface();
+        return static_cast<Texture2D*>(GetScreenBuffer(width, height, Graphics::GetDepthStencilFormat(), multiSample, autoResolve,
+            false, false, false))->GetRenderSurface();
     }
     }
 }
 }
 
 
@@ -1948,9 +1967,11 @@ void Renderer::BlurShadowMap(View* view, Texture2D* shadowMap, float blurScale)
     graphics_->SetScissorTest(false);
     graphics_->SetScissorTest(false);
 
 
     // Get a temporary render buffer
     // Get a temporary render buffer
-    Texture2D* tmpBuffer = static_cast<Texture2D*>(GetScreenBuffer(shadowMap->GetWidth(), shadowMap->GetHeight(), shadowMap->GetFormat(), false, false, false));
+    Texture2D* tmpBuffer = static_cast<Texture2D*>(GetScreenBuffer(shadowMap->GetWidth(), shadowMap->GetHeight(),
+        shadowMap->GetFormat(), 1, false, false, false, false));
     graphics_->SetRenderTarget(0, tmpBuffer->GetRenderSurface());
     graphics_->SetRenderTarget(0, tmpBuffer->GetRenderSurface());
-    graphics_->SetDepthStencil(GetDepthStencil(shadowMap->GetWidth(), shadowMap->GetHeight()));
+    graphics_->SetDepthStencil(GetDepthStencil(shadowMap->GetWidth(), shadowMap->GetHeight(), shadowMap->GetMultiSample(),
+        shadowMap->GetAutoResolve()));
     graphics_->SetViewport(IntRect(0, 0, shadowMap->GetWidth(), shadowMap->GetHeight()));
     graphics_->SetViewport(IntRect(0, 0, shadowMap->GetWidth(), shadowMap->GetHeight()));
 
 
     // Get shaders
     // Get shaders

+ 10 - 3
Source/Urho3D/Graphics/Renderer.h

@@ -212,6 +212,8 @@ public:
     void SetShadowSoftness(float shadowSoftness);
     void SetShadowSoftness(float shadowSoftness);
     /// Set shadow parameters when VSM is used, they help to reduce light bleeding. LightBleeding must be in [0, 1[
     /// Set shadow parameters when VSM is used, they help to reduce light bleeding. LightBleeding must be in [0, 1[
     void SetVSMShadowParameters(float minVariance, float lightBleedingReduction);
     void SetVSMShadowParameters(float minVariance, float lightBleedingReduction);
+    /// Set VSM shadow map multisampling level. Default 1 (no multisampling.)
+    void SetVSMMultiSample(int multiSample);
     /// Set post processing filter to the shadow map
     /// Set post processing filter to the shadow map
     void SetShadowMapFilter(Object* instance, ShadowMapFilter functionPtr);
     void SetShadowMapFilter(Object* instance, ShadowMapFilter functionPtr);
     /// Set reuse of shadow maps. Default is true. If disabled, also transparent geometry can be shadowed.
     /// Set reuse of shadow maps. Default is true. If disabled, also transparent geometry can be shadowed.
@@ -286,9 +288,12 @@ public:
     /// Return shadow softness.
     /// Return shadow softness.
     float GetShadowSoftness() const { return shadowSoftness_; }
     float GetShadowSoftness() const { return shadowSoftness_; }
 
 
-    /// Return VSM shadow parameters
+    /// Return VSM shadow parameters.
     Vector2 GetVSMShadowParameters() const { return vsmShadowParams_; };
     Vector2 GetVSMShadowParameters() const { return vsmShadowParams_; };
 
 
+    /// Return VSM shadow multisample level.
+    int GetVSMMultiSample() const { return vsmMultiSample_; }
+
     /// Return whether shadow maps are reused.
     /// Return whether shadow maps are reused.
     bool GetReuseShadowMaps() const { return reuseShadowMaps_; }
     bool GetReuseShadowMaps() const { return reuseShadowMaps_; }
 
 
@@ -389,9 +394,9 @@ public:
     Texture2D* GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight);
     Texture2D* GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight);
     /// Allocate a rendertarget or depth-stencil texture for deferred rendering or postprocessing. Should only be called during actual rendering, not before.
     /// Allocate a rendertarget or depth-stencil texture for deferred rendering or postprocessing. Should only be called during actual rendering, not before.
     Texture* GetScreenBuffer
     Texture* GetScreenBuffer
-        (int width, int height, unsigned format, bool cubemap, bool filtered, bool srgb, unsigned persistentKey = 0);
+        (int width, int height, unsigned format, int multiSample, bool autoResolve, bool cubemap, bool filtered, bool srgb, unsigned persistentKey = 0);
     /// Allocate a depth-stencil surface that does not need to be readable. Should only be called during actual rendering, not before.
     /// Allocate a depth-stencil surface that does not need to be readable. Should only be called during actual rendering, not before.
-    RenderSurface* GetDepthStencil(int width, int height);
+    RenderSurface* GetDepthStencil(int width, int height, int multiSample, bool autoResolve);
     /// Allocate an occlusion buffer.
     /// Allocate an occlusion buffer.
     OcclusionBuffer* GetOcclusionBuffer(Camera* camera);
     OcclusionBuffer* GetOcclusionBuffer(Camera* camera);
     /// Allocate a temporary shadow camera and a scene node for it. Is thread-safe.
     /// Allocate a temporary shadow camera and a scene node for it. Is thread-safe.
@@ -545,6 +550,8 @@ private:
     float shadowSoftness_;
     float shadowSoftness_;
     /// Shadow parameters when VSM is used, they help to reduce light bleeding.
     /// Shadow parameters when VSM is used, they help to reduce light bleeding.
     Vector2 vsmShadowParams_;
     Vector2 vsmShadowParams_;
+    /// Multisample level for VSM shadows.
+    int vsmMultiSample_;
     /// Maximum number of shadow maps per resolution.
     /// Maximum number of shadow maps per resolution.
     int maxShadowMaps_;
     int maxShadowMaps_;
     /// Minimum number of instances required in a batch group to render as instanced.
     /// Minimum number of instances required in a batch group to render as instanced.

+ 5 - 1
Source/Urho3D/Graphics/Texture.cpp

@@ -59,6 +59,7 @@ Texture::Texture(Context* context) :
     GPUObject(GetSubsystem<Graphics>()),
     GPUObject(GetSubsystem<Graphics>()),
     shaderResourceView_(0),
     shaderResourceView_(0),
     sampler_(0),
     sampler_(0),
+    resolveTexture_(0),
     format_(0),
     format_(0),
     usage_(TEXTURE_STATIC),
     usage_(TEXTURE_STATIC),
     levels_(0),
     levels_(0),
@@ -69,8 +70,11 @@ Texture::Texture(Context* context) :
     shadowCompare_(false),
     shadowCompare_(false),
     filterMode_(FILTER_DEFAULT),
     filterMode_(FILTER_DEFAULT),
     anisotropy_(0),
     anisotropy_(0),
+    multiSample_(1),
     sRGB_(false),
     sRGB_(false),
-    parametersDirty_(true)
+    parametersDirty_(true),
+    autoResolve_(false),
+    resolveDirty_(false)
 {
 {
     for (int i = 0; i < MAX_COORDS; ++i)
     for (int i = 0; i < MAX_COORDS; ++i)
         addressMode_[i] = ADDRESS_WRAP;
         addressMode_[i] = ADDRESS_WRAP;

+ 23 - 0
Source/Urho3D/Graphics/Texture.h

@@ -99,6 +99,15 @@ public:
     /// Return whether is using sRGB sampling and writing.
     /// Return whether is using sRGB sampling and writing.
     bool GetSRGB() const { return sRGB_; }
     bool GetSRGB() const { return sRGB_; }
 
 
+    /// Return texture multisampling level (1 = no multisampling).
+    int GetMultiSample() const { return multiSample_; }
+
+    /// Return texture multisampling autoresolve mode. When true, the texture is resolved before being sampled on SetTexture(). When false, the texture will not be resolved and must be read as individual samples in the shader.
+    bool GetAutoResolve() const { return autoResolve_; }
+
+    /// Return whether multisampled texture needs resolve.
+    bool IsResolveDirty() const { return resolveDirty_; }
+    
     /// Return backup texture.
     /// Return backup texture.
     Texture* GetBackupTexture() const { return backupTexture_; }
     Texture* GetBackupTexture() const { return backupTexture_; }
 
 
@@ -141,12 +150,18 @@ public:
     /// Return sampler state object. Only used on Direct3D11.
     /// Return sampler state object. Only used on Direct3D11.
     void* GetSampler() const { return sampler_; }
     void* GetSampler() const { return sampler_; }
 
 
+    /// Return resolve texture. Only used on Direct3D11.
+    void* GetResolveTexture() const { return resolveTexture_; }
+
     /// Return texture's target. Only used on OpenGL.
     /// Return texture's target. Only used on OpenGL.
     unsigned GetTarget() const { return target_; }
     unsigned GetTarget() const { return target_; }
 
 
     /// Convert format to sRGB. Not used on Direct3D9.
     /// Convert format to sRGB. Not used on Direct3D9.
     unsigned GetSRGBFormat(unsigned format);
     unsigned GetSRGBFormat(unsigned format);
 
 
+    /// Set or clear the need resolve flag. Called internally by Graphics.
+    void SetResolveDirty(bool enable) { resolveDirty_ = enable; }
+
     /// Check maximum allowed mip levels for a specific texture size.
     /// Check maximum allowed mip levels for a specific texture size.
     static unsigned CheckMaxLevels(int width, int height, unsigned requestedLevels);
     static unsigned CheckMaxLevels(int width, int height, unsigned requestedLevels);
     /// Check maximum allowed mip levels for a specific 3D texture size.
     /// Check maximum allowed mip levels for a specific 3D texture size.
@@ -176,6 +191,8 @@ protected:
 
 
     /// Direct3D11 sampler state object.
     /// Direct3D11 sampler state object.
     void* sampler_;
     void* sampler_;
+    /// Direct3D11 resolve texture object when multisample with autoresolve is used.
+    void* resolveTexture_;
 
 
     /// Texture format.
     /// Texture format.
     unsigned format_;
     unsigned format_;
@@ -203,10 +220,16 @@ protected:
     unsigned mipsToSkip_[MAX_TEXTURE_QUALITY_LEVELS];
     unsigned mipsToSkip_[MAX_TEXTURE_QUALITY_LEVELS];
     /// Border color.
     /// Border color.
     Color borderColor_;
     Color borderColor_;
+    /// Multisampling level.
+    int multiSample_;
     /// sRGB sampling and writing mode flag.
     /// sRGB sampling and writing mode flag.
     bool sRGB_;
     bool sRGB_;
     /// Parameters dirty flag.
     /// Parameters dirty flag.
     bool parametersDirty_;
     bool parametersDirty_;
+    /// Multisampling autoresolve flag.
+    bool autoResolve_;
+    /// Multisampling resolve needed -flag.
+    bool resolveDirty_;
     /// Backup texture.
     /// Backup texture.
     SharedPtr<Texture> backupTexture_;
     SharedPtr<Texture> backupTexture_;
 };
 };

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

@@ -109,7 +109,7 @@ bool Texture2D::EndLoad()
     return success;
     return success;
 }
 }
 
 
-bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usage)
+bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usage, int multiSample, bool autoResolve)
 {
 {
     if (width <= 0 || height <= 0)
     if (width <= 0 || height <= 0)
     {
     {
@@ -117,6 +117,15 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
         return false;
         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 or depth-stencil textures");
+        return false;
+    }
+
     // Delete the old rendersurface if any
     // Delete the old rendersurface if any
     renderSurface_.Reset();
     renderSurface_.Reset();
 
 
@@ -141,6 +150,8 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
     width_ = width;
     width_ = width;
     height_ = height;
     height_ = height;
     format_ = format;
     format_ = format;
+    multiSample_ = multiSample;
+    autoResolve_ = autoResolve;
 
 
     return Create();
     return Create();
 }
 }

+ 5 - 2
Source/Urho3D/Graphics/Texture2D.h

@@ -56,8 +56,11 @@ public:
     /// Release the texture.
     /// Release the texture.
     virtual void Release();
     virtual void Release();
 
 
-    /// Set size, format and usage. Zero size will follow application window size. Return true if successful.
-    bool SetSize(int width, int height, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set size, format, usage and multisampling parameters for rendertargets. Zero size will follow application window size. Return true if successful.
+	/** Autoresolve true means the texture will be automatically resolved to 1-sample after being rendered to and before being sampled as a texture. 
+		Autoresolve false means that texture will be read as individual samples in the shader and is not supported on Direct3D9.
+	 */
+    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.
     /// 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);
     bool SetData(unsigned level, int x, int y, int width, int height, const void* data);
     /// Set data from an image. Return true if successful. Optionally make a single channel image alpha-only.
     /// Set data from an image. Return true if successful. Optionally make a single channel image alpha-only.

+ 11 - 2
Source/Urho3D/Graphics/TextureCube.cpp

@@ -258,7 +258,7 @@ bool TextureCube::EndLoad()
     return true;
     return true;
 }
 }
 
 
-bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
+bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage, int multiSample)
 {
 {
     if (size <= 0)
     if (size <= 0)
     {
     {
@@ -267,7 +267,14 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
     }
     }
     if (usage == TEXTURE_DEPTHSTENCIL)
     if (usage == TEXTURE_DEPTHSTENCIL)
     {
     {
-        URHO3D_LOGERROR("Depth-stencil usage not supported for cube maps");
+        URHO3D_LOGERROR("Depth-stencil usage not supported for cube textures");
+        return false;
+    }
+
+    multiSample = Clamp(multiSample, 1, 16);
+    if (multiSample > 1 && usage < TEXTURE_RENDERTARGET)
+    {
+        URHO3D_LOGERROR("Multisampling is only supported for rendertarget cube textures");
         return false;
         return false;
     }
     }
 
 
@@ -303,6 +310,8 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
     width_ = size;
     width_ = size;
     height_ = size;
     height_ = size;
     format_ = format;
     format_ = format;
+    multiSample_ = multiSample;
+    autoResolve_ = multiSample > 1;
 
 
     return Create();
     return Create();
 }
 }

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

@@ -56,8 +56,8 @@ public:
     /// Release the texture.
     /// Release the texture.
     virtual void Release();
     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 parameter for rendertargets. Note that cube textures always use autoresolve when multisampled due to lacking support (on all APIs) to multisample them in a shader. Return true if successful.
+    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC, int multiSample = 1);
     /// Set data either partially or fully on a face's mip level. Return true if successful.
     /// 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);
     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.
     /// Set data of one face from a stream. Return true if successful.

+ 15 - 11
Source/Urho3D/Graphics/View.cpp

@@ -1711,7 +1711,7 @@ void View::SetRenderTargets(RenderPathCommand& command)
                 if (!depthOnlyDummyTexture_)
                 if (!depthOnlyDummyTexture_)
                 {
                 {
                     depthOnlyDummyTexture_ = renderer_->GetScreenBuffer(texture->GetWidth(), texture->GetHeight(),
                     depthOnlyDummyTexture_ = renderer_->GetScreenBuffer(texture->GetWidth(), texture->GetHeight(),
-                        graphics_->GetDummyColorFormat(), false, false, false);
+                        graphics_->GetDummyColorFormat(), texture->GetMultiSample(), texture->GetAutoResolve(), false, false, false);
                 }
                 }
 #endif
 #endif
                 graphics_->SetRenderTarget(0, GetRenderSurfaceFromTexture(depthOnlyDummyTexture_));
                 graphics_->SetRenderTarget(0, GetRenderSurfaceFromTexture(depthOnlyDummyTexture_));
@@ -2006,22 +2006,23 @@ void View::AllocateScreenBuffers()
             ++numViewportTextures;
             ++numViewportTextures;
     }
     }
 
 
-    // Allocate screen buffers with filtering active in case the quad commands need that
+    // Allocate screen buffers. Enable filtering in case the quad commands need that
     // Follow the sRGB mode of the destination render target
     // Follow the sRGB mode of the destination render target
     bool sRGB = renderTarget_ ? renderTarget_->GetParentTexture()->GetSRGB() : graphics_->GetSRGB();
     bool sRGB = renderTarget_ ? renderTarget_->GetParentTexture()->GetSRGB() : graphics_->GetSRGB();
+    int multiSample = renderTarget_ ? renderTarget_->GetMultiSample() : graphics_->GetMultiSample();
+    bool autoResolve = renderTarget_ ? renderTarget_->GetAutoResolve() : true;
     substituteRenderTarget_ = needSubstitute ? GetRenderSurfaceFromTexture(renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_,
     substituteRenderTarget_ = needSubstitute ? GetRenderSurfaceFromTexture(renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_,
-        format, false, true, sRGB)) : (RenderSurface*)0;
+        format, multiSample, autoResolve, false, true, sRGB)) : (RenderSurface*)0;
     for (unsigned i = 0; i < MAX_VIEWPORT_TEXTURES; ++i)
     for (unsigned i = 0; i < MAX_VIEWPORT_TEXTURES; ++i)
     {
     {
-        viewportTextures_[i] =
-            i < numViewportTextures ? renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_, format, false, true, sRGB) :
-                (Texture*)0;
+        viewportTextures_[i] = i < numViewportTextures ? renderer_->GetScreenBuffer(viewSize_.x_, viewSize_.y_, format, multiSample,
+            autoResolve, false, true, sRGB) : (Texture*)0;
     }
     }
     // If using a substitute render target and pingponging, the substitute can act as the second viewport texture
     // If using a substitute render target and pingponging, the substitute can act as the second viewport texture
     if (numViewportTextures == 1 && substituteRenderTarget_)
     if (numViewportTextures == 1 && substituteRenderTarget_)
         viewportTextures_[1] = substituteRenderTarget_->GetParentTexture();
         viewportTextures_[1] = substituteRenderTarget_->GetParentTexture();
 
 
-    // Allocate extra render targets defined by the rendering path
+    // Allocate extra render targets defined by the render path
     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
     for (unsigned i = 0; i < renderPath_->renderTargets_.Size(); ++i)
     {
     {
         const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
         const RenderTargetInfo& rtInfo = renderPath_->renderTargets_[i];
@@ -2047,8 +2048,9 @@ void View::AllocateScreenBuffers()
 
 
         // If the rendertarget is persistent, key it with a hash derived from the RT name and the view's pointer
         // If the rendertarget is persistent, key it with a hash derived from the RT name and the view's pointer
         renderTargets_[rtInfo.name_] =
         renderTargets_[rtInfo.name_] =
-            renderer_->GetScreenBuffer(intWidth, intHeight, rtInfo.format_, rtInfo.cubemap_, rtInfo.filtered_, rtInfo.sRGB_,
-                rtInfo.persistent_ ? StringHash(rtInfo.name_).Value() + (unsigned)(size_t)this : 0);
+            renderer_->GetScreenBuffer(intWidth, intHeight, rtInfo.format_, rtInfo.multiSample_, rtInfo.autoResolve_,
+                rtInfo.cubemap_, rtInfo.filtered_, rtInfo.sRGB_, rtInfo.persistent_ ? StringHash(rtInfo.name_).Value()
+                + (unsigned)(size_t)this : 0);
     }
     }
 }
 }
 
 
@@ -3016,7 +3018,8 @@ void View::RenderShadowMap(const LightBatchQueue& queue)
         // Disable other render targets
         // Disable other render targets
         for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
         for (unsigned i = 1; i < MAX_RENDERTARGETS; ++i)
             graphics_->SetRenderTarget(i, (RenderSurface*) 0);
             graphics_->SetRenderTarget(i, (RenderSurface*) 0);
-        graphics_->SetDepthStencil(renderer_->GetDepthStencil(shadowMap->GetWidth(), shadowMap->GetHeight()));
+        graphics_->SetDepthStencil(renderer_->GetDepthStencil(shadowMap->GetWidth(), shadowMap->GetHeight(),
+            shadowMap->GetMultiSample(), shadowMap->GetAutoResolve()));
         graphics_->SetViewport(IntRect(0, 0, shadowMap->GetWidth(), shadowMap->GetHeight()));
         graphics_->SetViewport(IntRect(0, 0, shadowMap->GetWidth(), shadowMap->GetHeight()));
         graphics_->Clear(CLEAR_DEPTH | CLEAR_COLOR, Color::WHITE);
         graphics_->Clear(CLEAR_DEPTH | CLEAR_COLOR, Color::WHITE);
 
 
@@ -3073,7 +3076,8 @@ RenderSurface* View::GetDepthStencil(RenderSurface* renderTarget)
     RenderSurface* depthStencil = renderTarget->GetLinkedDepthStencil();
     RenderSurface* depthStencil = renderTarget->GetLinkedDepthStencil();
     // Finally get one from Renderer
     // Finally get one from Renderer
     if (!depthStencil)
     if (!depthStencil)
-        depthStencil = renderer_->GetDepthStencil(renderTarget->GetWidth(), renderTarget->GetHeight());
+        depthStencil = renderer_->GetDepthStencil(renderTarget->GetWidth(), renderTarget->GetHeight(),
+            renderTarget->GetMultiSample(), renderTarget->GetAutoResolve());
     return depthStencil;
     return depthStencil;
 }
 }
 
 

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

@@ -36,6 +36,8 @@ struct RenderTargetInfo
     unsigned format_ @ format;
     unsigned format_ @ format;
     Vector2 size_ @ size;
     Vector2 size_ @ size;
     RenderTargetSizeMode sizeMode_ @ sizeMode;
     RenderTargetSizeMode sizeMode_ @ sizeMode;
+    int multiSample_ @ multiSample;
+    bool autoResolve_ @ autoResolve;
     bool enabled_ @ enabled;
     bool enabled_ @ enabled;
     bool cubemap_ @ cubemap;
     bool cubemap_ @ cubemap;
     bool filtered_ @ filtered;
     bool filtered_ @ filtered;

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

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

+ 4 - 1
Source/Urho3D/LuaScript/pkgs/Graphics/Renderer.pkg

@@ -18,6 +18,7 @@ class Renderer
     void SetShadowQuality(ShadowQuality quality);
     void SetShadowQuality(ShadowQuality quality);
     void SetShadowSoftness(float shadowSoftness);
     void SetShadowSoftness(float shadowSoftness);
     void SetVSMShadowParameters(float minVariance, float lightBleedingReduction);
     void SetVSMShadowParameters(float minVariance, float lightBleedingReduction);
+    void SetVSMMultiSample(int multiSample);
     void SetReuseShadowMaps(bool enable);
     void SetReuseShadowMaps(bool enable);
     void SetMaxShadowMaps(int shadowMaps);
     void SetMaxShadowMaps(int shadowMaps);
     void SetDynamicInstancing(bool enable);
     void SetDynamicInstancing(bool enable);
@@ -32,7 +33,7 @@ class Renderer
     void SetMobileShadowBiasAdd(float add);
     void SetMobileShadowBiasAdd(float add);
     void SetMobileNormalOffsetMul(float mul);
     void SetMobileNormalOffsetMul(float mul);
     void ReloadShaders();
     void ReloadShaders();
-    
+
     unsigned GetNumViewports() const;
     unsigned GetNumViewports() const;
     Viewport* GetViewport(unsigned index) const;
     Viewport* GetViewport(unsigned index) const;
     RenderPath* GetDefaultRenderPath() const;
     RenderPath* GetDefaultRenderPath() const;
@@ -48,6 +49,7 @@ class Renderer
     ShadowQuality GetShadowQuality() const;
     ShadowQuality GetShadowQuality() const;
     float GetShadowSoftness() const;
     float GetShadowSoftness() const;
     Vector2 GetVSMShadowParameters() const;
     Vector2 GetVSMShadowParameters() const;
+    int GetVSMMultiSample() const;
     bool GetReuseShadowMaps() const;
     bool GetReuseShadowMaps() const;
     int GetMaxShadowMaps() const;
     int GetMaxShadowMaps() const;
     bool GetDynamicInstancing() const;
     bool GetDynamicInstancing() const;
@@ -88,6 +90,7 @@ class Renderer
     tolua_property__get_set int shadowMapSize;
     tolua_property__get_set int shadowMapSize;
     tolua_property__get_set ShadowQuality shadowQuality;
     tolua_property__get_set ShadowQuality shadowQuality;
     tolua_property__get_set float shadowSoftness;
     tolua_property__get_set float shadowSoftness;
+    tolua_property__get_set int VSMMultiSample;
     tolua_property__get_set bool reuseShadowMaps;
     tolua_property__get_set bool reuseShadowMaps;
     tolua_property__get_set int maxShadowMaps;
     tolua_property__get_set int maxShadowMaps;
     tolua_property__get_set bool dynamicInstancing;
     tolua_property__get_set bool dynamicInstancing;

+ 6 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/Texture.pkg

@@ -21,6 +21,9 @@ class Texture : public Resource
     unsigned GetAnisotropy() const;
     unsigned GetAnisotropy() const;
     const Color& GetBorderColor() const;
     const Color& GetBorderColor() const;
     bool GetSRGB() const;
     bool GetSRGB() const;
+    int GetMultiSample() const;
+	bool GetAutoResolve() const;
+	bool IsResolveDirty() const;
     Texture* GetBackupTexture() const;
     Texture* GetBackupTexture() const;
     int GetMipsToSkip(int quality) const;
     int GetMipsToSkip(int quality) const;
     int GetLevelWidth(unsigned level) const;
     int GetLevelWidth(unsigned level) const;
@@ -40,6 +43,9 @@ class Texture : public Resource
     tolua_property__get_set unsigned anisotropy;
     tolua_property__get_set unsigned anisotropy;
     tolua_property__get_set Color& borderColor;
     tolua_property__get_set Color& borderColor;
     tolua_property__get_set bool sRGB;
     tolua_property__get_set bool sRGB;
+    tolua_readonly tolua_property__get_set int multiSample;
+    tolua_readonly tolua_property__get_set bool autoResolve;
+    tolua_readonly tolua_property__is_set bool resolveDirty;
     tolua_property__get_set Texture* backupTexture;
     tolua_property__get_set Texture* backupTexture;
     tolua_readonly tolua_property__get_set TextureUsage usage;
     tolua_readonly tolua_property__get_set TextureUsage usage;
 };
 };

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

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

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

@@ -5,7 +5,7 @@ class TextureCube : public Texture
     TextureCube();
     TextureCube();
     ~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 SetData(CubeMapFace face, Image* image, bool useAlpha = false);
     bool SetData(CubeMapFace face, Image* image, bool useAlpha = false);
 
 
     RenderSurface* GetRenderSurface(CubeMapFace face) const;
     RenderSurface* GetRenderSurface(CubeMapFace face) const;