From 4949616691fbe44bd1e543dd74009768b85acfa4 Mon Sep 17 00:00:00 2001 From: Vladimir Vukicevic Date: Wed, 9 Jul 2014 12:26:51 -0700 Subject: [PATCH] Bug 1036602 - add D3D11 support for VR rendering; r=bas From fbb1e2ba9936896a1eb932585218401f4c95e411 Mon Sep 17 00:00:00 2001 --- gfx/layers/d3d11/CompositorD3D11.cpp | 212 +++++++++++++++++++++++++++++++++- gfx/layers/d3d11/CompositorD3D11.fx | 1 + gfx/layers/d3d11/CompositorD3D11.h | 9 ++ gfx/layers/d3d11/CompositorD3D11VR.fx | 71 ++++++++++++ 4 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 gfx/layers/d3d11/CompositorD3D11VR.fx --- gfx/layers/d3d11/CompositorD3D11.cpp | 212 +++++++++++++++++++++++++- gfx/layers/d3d11/CompositorD3D11.fx | 1 + gfx/layers/d3d11/CompositorD3D11.h | 9 ++ gfx/layers/d3d11/CompositorD3D11VR.fx | 71 +++++++++ 4 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 gfx/layers/d3d11/CompositorD3D11VR.fx diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp index 11225837b30..2e3e196cdc5 100644 --- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -7,6 +7,7 @@ #include "TextureD3D11.h" #include "CompositorD3D11Shaders.h" +#include "CompositorD3D11ShadersVR.h" #include "gfxWindowsPlatform.h" #include "nsIWidget.h" @@ -16,6 +17,7 @@ #include "nsWindowsHelpers.h" #include "gfxPrefs.h" #include "gfxCrashReporterUtils.h" +#include "gfxVR.h" #include "mozilla/EnumeratedArray.h" @@ -67,6 +69,27 @@ struct DeviceAttachmentsD3D11 RefPtr mComponentBlendState; RefPtr mDisabledBlendState; RefPtr mSyncTexture; + + // + // VR pieces + // + RefPtr mVRDistortionInputLayout; + RefPtr mVRDistortionConstants; + + typedef EnumeratedArray> + VRVertexShaderArray; + typedef EnumeratedArray> + VRPixelShaderArray; + + VRVertexShaderArray mVRDistortionVS; + VRPixelShaderArray mVRDistortionPS; + + // These will be created/filled in as needed during rendering whenever the configuration + // changes. + VRHMDConfiguration mVRConfiguration; + RefPtr mVRDistortionVertices[2]; // one for each eye + RefPtr mVRDistortionIndices[2]; + uint32_t mVRDistortionIndexCount[2]; }; CompositorD3D11::CompositorD3D11(nsIWidget* aWidget) @@ -282,13 +305,34 @@ CompositorD3D11::Initialize() RefPtr texture; hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture)); - if (FAILED(hr)) { return false; } hr = texture->QueryInterface((IDXGIResource**)byRef(mAttachments->mSyncTexture)); + if (FAILED(hr)) { + return false; + } + + // + // VR additions + // + D3D11_INPUT_ELEMENT_DESC vrlayout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + hr = mDevice->CreateInputLayout(vrlayout, + sizeof(vrlayout) / sizeof(D3D11_INPUT_ELEMENT_DESC), + OculusVRDistortionVS, + sizeof(OculusVRDistortionVS), + byRef(mAttachments->mVRDistortionInputLayout)); + cBufferDesc.ByteWidth = sizeof(gfx::VRDistortionConstants); + hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, byRef(mAttachments->mVRDistortionConstants)); if (FAILED(hr)) { return false; } @@ -589,6 +633,141 @@ CompositorD3D11::ClearRect(const gfx::Rect& aRect) mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF); } +void +CompositorD3D11::DrawVRDistortion(const gfx::Rect& aRect, + const gfx::Rect& aClipRect, + const EffectChain& aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform) +{ + MOZ_ASSERT(aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION); + + if (aEffectChain.mSecondaryEffects[EffectTypes::MASK] || + aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) + { + NS_WARNING("DrawVRDistortion: ignoring secondary effect!"); + } + + HRESULT hr; + + EffectVRDistortion* vrEffect = + static_cast(aEffectChain.mPrimaryEffect.get()); + + TextureSourceD3D11* source = vrEffect->mTexture->AsSourceD3D11(); + gfx::IntSize size = vrEffect->mRenderTarget->GetSize(); // XXX source->GetSize() + + VRHMDInfo* hmdInfo = vrEffect->mHMD; + VRDistortionConstants shaderConstants; + + // do we need to recreate the VR buffers, since the config has changed? + if (hmdInfo->GetConfiguration() != mAttachments->mVRConfiguration) { + D3D11_SUBRESOURCE_DATA sdata = { 0 }; + CD3D11_BUFFER_DESC desc(0, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE); + + // XXX as an optimization, we should really pack the indices and vertices for both eyes + // into one buffer instead of needing one eye each. Then we can just bind them once. + for (uint32_t eye = 0; eye < 2; eye++) { + const gfx::VRDistortionMesh& mesh = hmdInfo->GetDistortionMesh(eye); + + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.ByteWidth = mesh.mVertices.Length() * sizeof(gfx::VRDistortionVertex); + sdata.pSysMem = mesh.mVertices.Elements(); + + hr = mDevice->CreateBuffer(&desc, &sdata, byRef(mAttachments->mVRDistortionVertices[eye])); + if (FAILED(hr)) { + NS_WARNING("CreateBuffer failed"); + return; + } + + desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + desc.ByteWidth = mesh.mIndices.Length() * sizeof(uint16_t); + sdata.pSysMem = mesh.mIndices.Elements(); + + hr = mDevice->CreateBuffer(&desc, &sdata, byRef(mAttachments->mVRDistortionIndices[eye])); + if (FAILED(hr)) { + NS_WARNING("CreateBuffer failed"); + return; + } + + mAttachments->mVRDistortionIndexCount[eye] = mesh.mIndices.Length(); + } + + mAttachments->mVRConfiguration = hmdInfo->GetConfiguration(); + } + + // XXX do I need to set a scissor rect? Is this the right scissor rect? + D3D11_RECT scissor; + scissor.left = aClipRect.x; + scissor.right = aClipRect.XMost(); + scissor.top = aClipRect.y; + scissor.bottom = aClipRect.YMost(); + mContext->RSSetScissorRects(1, &scissor); + + // Triangle lists and same layout for both eyes + mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + mContext->IASetInputLayout(mAttachments->mVRDistortionInputLayout); + + // Shaders for this HMD + mContext->VSSetShader(mAttachments->mVRDistortionVS[mAttachments->mVRConfiguration.hmdType], nullptr, 0); + mContext->PSSetShader(mAttachments->mVRDistortionPS[mAttachments->mVRConfiguration.hmdType], nullptr, 0); + + // This is the source texture SRV for the pixel shader + // XXX, um should we cache this SRV? + RefPtr view; + mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view)); + ID3D11ShaderResourceView* srView = view; + mContext->PSSetShaderResources(0, 1, &srView); + + + gfx::IntSize vpSizeInt = mCurrentRT->GetSize(); + gfx::Size vpSize(vpSizeInt.width, vpSizeInt.height); + ID3D11Buffer* vbuffer; + UINT vsize, voffset; + + for (uint32_t eye = 0; eye < 2; eye++) { + gfx::IntRect eyeViewport; + eyeViewport.x = eye * size.width / 2; + eyeViewport.y = 0; + eyeViewport.width = size.width / 2; + eyeViewport.height = size.height; + + hmdInfo->FillDistortionConstants(eye, + size, eyeViewport, + vpSize, aRect, + shaderConstants); + + // D3D has clip space top-left as -1,1 so we need to flip the Y coordinate offset here + shaderConstants.destinationScaleAndOffset[1] = - shaderConstants.destinationScaleAndOffset[1]; + + // XXX I really want to write a templated helper for these next 4 lines + D3D11_MAPPED_SUBRESOURCE resource; + mContext->Map(mAttachments->mVRDistortionConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource); + *(gfx::VRDistortionConstants*)resource.pData = shaderConstants; + mContext->Unmap(mAttachments->mVRDistortionConstants, 0); + + // XXX is there a better way to change a bunch of these things from what they were set to + // in BeginFrame/etc? + vbuffer = mAttachments->mVRDistortionVertices[eye]; + vsize = sizeof(gfx::VRDistortionVertex); + voffset = 0; + mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset); + mContext->IASetIndexBuffer(mAttachments->mVRDistortionIndices[eye], DXGI_FORMAT_R16_UINT, 0); + + ID3D11Buffer* constBuf = mAttachments->mVRDistortionConstants; + mContext->VSSetConstantBuffers(0, 1, &constBuf); + + mContext->DrawIndexed(mAttachments->mVRDistortionIndexCount[eye], 0, 0); + } + + // restore previous configurations + vbuffer = mAttachments->mVertexBuffer; + vsize = sizeof(Vertex); + voffset = 0; + mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset); + mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0); + mContext->IASetInputLayout(mAttachments->mInputLayout); +} + void CompositorD3D11::DrawQuad(const gfx::Rect& aRect, const gfx::Rect& aClipRect, @@ -601,6 +780,12 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect, } MOZ_ASSERT(mCurrentRT, "No render target"); + + if (aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION) { + DrawVRDistortion(aRect, aClipRect, aEffectChain, aOpacity, aTransform); + return; + } + memcpy(&mVSConstants.layerTransform, &aTransform._11, 64); IntPoint origin = mCurrentRT->GetOrigin(); mVSConstants.renderTargetOffset[0] = origin.x; @@ -966,7 +1151,7 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize) { D3D11_VIEWPORT viewport; viewport.MaxDepth = 1.0f; - viewport.MinDepth = 0; + viewport.MinDepth = 0.0f; viewport.Width = aSize.width; viewport.Height = aSize.height; viewport.TopLeftX = 0; @@ -974,6 +1159,8 @@ CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize) mContext->RSSetViewports(1, &viewport); + // This view matrix translates coordinates from 0..width and 0..height to + // -1..1 on the X axis, and -1..1 on the Y axis (flips the Y coordinate) Matrix viewMatrix = Matrix::Translation(-1.0, 1.0); viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height)); viewMatrix.PreScale(1.0f, -1.0f); @@ -1113,6 +1300,25 @@ CompositorD3D11::CreateShaders() return false; } + + /* VR stuff */ + + hr = mDevice->CreateVertexShader(OculusVRDistortionVS, + sizeof(OculusVRDistortionVS), + nullptr, + byRef(mAttachments->mVRDistortionVS[VRHMDType::Oculus])); + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader(OculusVRDistortionPS, + sizeof(OculusVRDistortionPS), + nullptr, + byRef(mAttachments->mVRDistortionPS[VRHMDType::Oculus])); + if (FAILED(hr)) { + return false; + } + return true; } diff --git a/gfx/layers/d3d11/CompositorD3D11.fx b/gfx/layers/d3d11/CompositorD3D11.fx index ca189b5a028..e5a586e8f34 100644 --- a/gfx/layers/d3d11/CompositorD3D11.fx +++ b/gfx/layers/d3d11/CompositorD3D11.fx @@ -71,6 +71,7 @@ SamplerState LayerTextureSamplerLinear float4 TransformedPosition(float2 aInPosition) { // the current vertex's position on the quad + // [x,y,0,1] is mandated by the CSS Transforms spec as the point value to transform float4 position = float4(0, 0, 0, 1); // We use 4 component floats to uniquely describe a rectangle, by the structure diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h index 86510912e28..f2a00292eef 100644 --- a/gfx/layers/d3d11/CompositorD3D11.h +++ b/gfx/layers/d3d11/CompositorD3D11.h @@ -27,6 +27,8 @@ struct VertexShaderConstants gfx::Rect textureCoords; gfx::Rect layerQuad; gfx::Rect maskQuad; + float vrEyeToSourceUVScale[2]; + float vrEyeToSourceUVOffset[2]; }; struct PixelShaderConstants @@ -94,6 +96,13 @@ public: gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE; + /* Helper for when the primary effect is VR_DISTORTION */ + void DrawVRDistortion(const gfx::Rect &aRect, + const gfx::Rect &aClipRect, + const EffectChain &aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4 &aTransform); + /** * Start a new frame. If aClipRectIn is null, sets *aClipRectOut to the * screen dimensions. diff --git a/gfx/layers/d3d11/CompositorD3D11VR.fx b/gfx/layers/d3d11/CompositorD3D11VR.fx new file mode 100644 index 00000000000..9a129ebc551 --- /dev/null +++ b/gfx/layers/d3d11/CompositorD3D11VR.fx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* These constants should match mozilla:gfx::VRDistortionConstants */ +float4 VREyeToSource : register(vs, c0); +float4 VRDestinationScaleAndOffset : register(vs, c1); + +/* This is the input undistorted texture */ +Texture2D Texture : register(ps, t0); + +/* This maps to mozilla::gfx::VRDistortionVertex in gfxVR.h. + * It's shared amongst all of the different rendering types; + * some might not be in use for a particular distortion effect. + */ +struct VS_VR_INPUT { + float2 vPosition : POSITION; + float2 vTexCoord0 : TEXCOORD0; + float2 vTexCoord1 : TEXCOORD1; + float2 vTexCoord2 : TEXCOORD2; + float4 vGenericAttribs : COLOR0; +}; + +struct VS_VR_OUTPUT { + float4 vPosition : SV_Position; + float3 vTexCoord0 : TEXCOORD0; + float3 vTexCoord1 : TEXCOORD1; + float3 vTexCoord2 : TEXCOORD2; + float4 vGenericAttribs : COLOR; +}; + +SamplerState Linear +{ + Filter = MIN_MAG_MIP_LINEAR; + AddressU = Clamp; + AddressV = Clamp; +}; + +VS_VR_OUTPUT OculusVRDistortionVS(const VS_VR_INPUT aVertex) +{ + VS_VR_OUTPUT res; + + float2 tc0 = VREyeToSource.xy * aVertex.vTexCoord0 + VREyeToSource.zw; + float2 tc1 = VREyeToSource.xy * aVertex.vTexCoord1 + VREyeToSource.zw; + float2 tc2 = VREyeToSource.xy * aVertex.vTexCoord2 + VREyeToSource.zw; + + //res.vPosition.xy = aVertex.vPosition.xy; + res.vPosition.xy = aVertex.vPosition.xy * VRDestinationScaleAndOffset.zw + VRDestinationScaleAndOffset.xy; + res.vPosition.zw = float2(0.5, 1.0); + + res.vTexCoord0 = float3(tc0, 1); + res.vTexCoord1 = float3(tc1, 1); + res.vTexCoord2 = float3(tc2, 1); + + res.vGenericAttribs = aVertex.vGenericAttribs; + + return res; +} + +float4 OculusVRDistortionPS(const VS_VR_OUTPUT aVertex) : SV_Target +{ + float resR = Texture.Sample(Linear, aVertex.vTexCoord0.xy).r; + float resG = Texture.Sample(Linear, aVertex.vTexCoord1.xy).g; + float resB = Texture.Sample(Linear, aVertex.vTexCoord2.xy).b; + + return float4(resR * aVertex.vGenericAttribs.r, + resG * aVertex.vGenericAttribs.r, + resB * aVertex.vGenericAttribs.r, + 1.0); +}