Use a runtime test for constant buffer offsetting. (bug 1379413 part 1, r=bas)
authorDavid Anderson <danderson@mozilla.com>
Tue, 11 Jul 2017 00:13:21 -0700
changeset 368223 3cbaaeb4f8c235b75721e2137d5687780edfecce
parent 368222 43260cd0305bb0ec45cfafa416ac6d01b2ecc0d7
child 368224 0b438dce0908ba9dfd2f25e8486151603f22aa52
push id32159
push usercbook@mozilla.com
push dateTue, 11 Jul 2017 10:52:11 +0000
treeherdermozilla-central@b07db5d650b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1379413
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Use a runtime test for constant buffer offsetting. (bug 1379413 part 1, r=bas)
gfx/layers/d3d11/MLGDeviceD3D11.cpp
gfx/layers/d3d11/MLGDeviceD3D11.h
gfx/layers/d3d11/mlgshaders/shaders.manifest
gfx/layers/d3d11/mlgshaders/test-features-vs.hlsl
gfx/layers/mlgpu/MLGDevice.cpp
gfx/layers/mlgpu/MLGDevice.h
--- a/gfx/layers/d3d11/MLGDeviceD3D11.cpp
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.cpp
@@ -1427,24 +1427,38 @@ MLGDeviceD3D11::SetScissorRect(const May
 
 void
 MLGDeviceD3D11::SetVertexShader(VertexShaderID aShader)
 {
   if (!mVertexShaders[aShader]) {
     InitVertexShader(aShader);
     MOZ_ASSERT(mInputLayouts[aShader]);
   }
-  if (mCurrentVertexShader != mVertexShaders[aShader]) {
-    mCtx->VSSetShader(mVertexShaders[aShader], nullptr, 0);
-    mCurrentVertexShader = mVertexShaders[aShader];
+  SetVertexShader(mVertexShaders[aShader]);
+  SetInputLayout(mInputLayouts[aShader]);
+}
+
+void
+MLGDeviceD3D11::SetInputLayout(ID3D11InputLayout* aLayout)
+{
+  if (mCurrentInputLayout == aLayout) {
+    return;
   }
-  if (mCurrentInputLayout != mInputLayouts[aShader]) {
-    mCtx->IASetInputLayout(mInputLayouts[aShader]);
-    mCurrentInputLayout = mInputLayouts[aShader];
+  mCtx->IASetInputLayout(aLayout);
+  mCurrentInputLayout = aLayout;
+}
+
+void
+MLGDeviceD3D11::SetVertexShader(ID3D11VertexShader* aShader)
+{
+  if (mCurrentVertexShader == aShader) {
+    return;
   }
+  mCtx->VSSetShader(aShader, nullptr, 0);
+  mCurrentVertexShader = aShader;
 }
 
 void
 MLGDeviceD3D11::SetPixelShader(PixelShaderID aShader)
 {
   if (!mPixelShaders[aShader]) {
     InitPixelShader(aShader);
   }
@@ -1958,16 +1972,140 @@ MLGDeviceD3D11::CopyTexture(MLGTexture* 
   D3D11_BOX box = RectToBox(aRect);
   mCtx->CopySubresourceRegion(
     dest->GetTexture(), 0,
     aTarget.x, aTarget.y, 0,
     source->GetTexture(), 0,
     &box);
 }
 
+bool
+MLGDeviceD3D11::VerifyConstantBufferOffsetting()
+{
+  RefPtr<ID3D11VertexShader> vs;
+  HRESULT hr = mDevice->CreateVertexShader(
+    sTestConstantBuffersVS.mData,
+    sTestConstantBuffersVS.mLength,
+    nullptr,
+    getter_AddRefs(vs));
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed creating vertex shader for buffer test: " << hexa(hr);
+    return false;
+  }
+
+  D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
+    { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
+  };
+
+  RefPtr<ID3D11InputLayout> layout;
+  hr = mDevice->CreateInputLayout(
+    inputDesc,
+    sizeof(inputDesc) / sizeof(inputDesc[0]),
+    sTestConstantBuffersVS.mData,
+    sTestConstantBuffersVS.mLength,
+    getter_AddRefs(layout));
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed creating input layout for buffer test: " << hexa(hr);
+    return false;
+  }
+
+  RefPtr<MLGRenderTarget> rt = CreateRenderTarget(IntSize(2, 2), MLGRenderTargetFlags::Default);
+  if (!rt) {
+    return false;
+  }
+
+  static const size_t kConstantSize = 4 * sizeof(float);
+  static const size_t kMinConstants = 16;
+  static const size_t kNumBindings = 3;
+
+  RefPtr<MLGBuffer> buffer = CreateBuffer(
+    MLGBufferType::Constant,
+    kConstantSize * kMinConstants * kNumBindings,
+    MLGUsage::Dynamic,
+    nullptr);
+  if (!buffer) {
+    return false;
+  }
+
+  // Populate the buffer. The shader will pick R from buffer 1, G from buffer
+  // 2, and B from buffer 3.
+  {
+    MLGMappedResource map;
+    if (!Map(buffer, MLGMapType::WRITE_DISCARD, &map)) {
+      return false;
+    }
+
+    *reinterpret_cast<Color*>(map.mData) =
+      Color(1.0f, 0.2f, 0.3f, 1.0f);
+    *reinterpret_cast<Color*>(map.mData + kConstantSize * kMinConstants) =
+      Color(0.4f, 0.0f, 0.5f, 1.0f);
+    *reinterpret_cast<Color*>(map.mData + (kConstantSize * kMinConstants) * 2) =
+      Color(0.6f, 0.7f, 1.0f, 1.0f);
+
+    Unmap(buffer);
+  }
+
+  Clear(rt, Color(0.0f, 0.0f, 0.0f, 1.0f));
+  SetRenderTarget(rt);
+  SetViewport(IntRect(0, 0, 2, 2));
+  SetScissorRect(Nothing());
+  SetBlendState(MLGBlendState::Over);
+
+  SetTopology(MLGPrimitiveTopology::UnitQuad);
+  SetInputLayout(layout);
+  SetVertexShader(vs);
+  SetPixelShader(PixelShaderID::ColoredQuad);
+
+  ID3D11Buffer* buffers[3] = {
+    buffer->AsD3D11()->GetBuffer(),
+    buffer->AsD3D11()->GetBuffer(),
+    buffer->AsD3D11()->GetBuffer()
+  };
+  UINT offsets[3] = { 0 * kMinConstants, 1 * kMinConstants, 2 * kMinConstants };
+  UINT counts[3] = { kMinConstants, kMinConstants, kMinConstants };
+
+  mCtx1->VSSetConstantBuffers1(0, 3, buffers, offsets, counts);
+  mCtx->Draw(4, 0);
+
+  // Kill bindings to resources.
+  SetRenderTarget(nullptr);
+
+  ID3D11Buffer* nulls[3] = { nullptr, nullptr, nullptr };
+  mCtx->VSSetConstantBuffers(0, 3, nulls);
+
+  RefPtr<MLGTexture> copy = CreateTexture(
+    IntSize(2, 2),
+    SurfaceFormat::B8G8R8A8,
+    MLGUsage::Staging,
+    MLGTextureFlags::None);
+  if (!copy) {
+    return false;
+  }
+
+  CopyTexture(copy, IntPoint(0, 0), rt->GetTexture(), IntRect(0, 0, 2, 2));
+
+  uint8_t r, g, b, a;
+  {
+    MLGMappedResource map;
+    if (!Map(copy, MLGMapType::READ, &map)) {
+      return false;
+    }
+    r = map.mData[0];
+    g = map.mData[1];
+    b = map.mData[2];
+    a = map.mData[3];
+    Unmap(copy);
+  }
+
+  return r == 255 &&
+         g == 0 &&
+         b == 255 &&
+         a == 255;
+}
+
 static D3D11_BOX
 RectToBox(const gfx::IntRect& aRect)
 {
   D3D11_BOX box;
   box.front = 0;
   box.back = 1;
   box.left = aRect.x;
   box.top = aRect.y;
--- a/gfx/layers/d3d11/MLGDeviceD3D11.h
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.h
@@ -267,16 +267,20 @@ private:
   bool InitInputLayout(D3D11_INPUT_ELEMENT_DESC* aDesc,
                        size_t aNumElements,
                        const ShaderBytes& aCode,
                        VertexShaderID aShaderID);
   bool InitRasterizerStates();
   bool InitSamplerStates();
   bool InitBlendStates();
   bool InitDepthStencilState();
+  bool VerifyConstantBufferOffsetting() override;
+
+  void SetInputLayout(ID3D11InputLayout* aLayout);
+  void SetVertexShader(ID3D11VertexShader* aShader);
 
 private:
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11DeviceContext> mCtx;
   RefPtr<ID3D11DeviceContext1> mCtx1;
   UniquePtr<DiagnosticsD3D11> mDiagnostics;
 
   typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders, RefPtr<ID3D11PixelShader>> PixelShaderArray;
--- a/gfx/layers/d3d11/mlgshaders/shaders.manifest
+++ b/gfx/layers/d3d11/mlgshaders/shaders.manifest
@@ -87,8 +87,12 @@
   shaders:
    - DiagnosticTextVS
 
 - type: ps_4_0
   file: diagnostics-ps.hlsl
   shaders:
    - DiagnosticTextPS
 
+- type: vs_4_0
+  file: test-features-vs.hlsl
+  shaders:
+   - TestConstantBuffersVS
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d11/mlgshaders/test-features-vs.hlsl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+#include "common-vs.hlsl"
+#include "color-common.hlsl"
+
+struct VS_INPUT {
+  float2 vPos : POSITION;
+};
+
+cbuffer Buffer0 : register(b0) {
+  float4 aValue0;
+};
+cbuffer Buffer1 : register(b1) {
+  float4 aValue1;
+};
+cbuffer Buffer2 : register(b2) {
+  float4 aValue2;
+};
+
+VS_COLOROUTPUT_CLIPPED TestConstantBuffersVS(VS_INPUT aInput)
+{
+  // Draw to the entire viewport.
+  float2 pos = UnitQuadToRect(aInput.vPos, float4(-1, -1, 2, 2));
+
+  VS_COLOROUTPUT_CLIPPED output;
+  output.vPosition = float4(pos, 0, 1);
+  output.vColor = float4(aValue0.r, aValue1.g, aValue2.b, 1.0);
+  return output;
+}
--- a/gfx/layers/mlgpu/MLGDevice.cpp
+++ b/gfx/layers/mlgpu/MLGDevice.cpp
@@ -81,21 +81,24 @@ MLGDevice::Initialize()
     return Fail("FEATURE_FAILURE_NO_MAX_CB_BIND_SIZE", "Failed to set a max constant buffer bind size");
   }
   if (mMaxConstantBufferBindSize < mlg::kMaxConstantBufferSize) {
     // StagingBuffer depends on this value being accurate, so for now we just
     // double-check it here.
     return Fail("FEATURE_FAILURE_MIN_MAX_CB_BIND_SIZE", "Minimum constant buffer bind size not met");
   }
 
-  // We allow this to be pref'd off for testing. Switching it on enables
+  // We allow this to be pref'd off for testing. Switching it off enables
   // Direct3D 11.0/Windows 7/OpenGL-style buffer code paths.
   if (!gfxPrefs::AdvancedLayersEnableBufferSharing()) {
     mCanUseConstantBufferOffsetBinding = false;
   }
+  if (mCanUseConstantBufferOffsetBinding) {
+    mCanUseConstantBufferOffsetBinding = VerifyConstantBufferOffsetting();
+  }
 
   // We allow this to be pref'd off for testing. Disabling it turns on
   // ID3D11DeviceContext1::ClearView support, which is present on
   // newer Windows 8+ drivers.
   if (!gfxPrefs::AdvancedLayersEnableClearView()) {
     mCanUseClearView = false;
   }
 
--- a/gfx/layers/mlgpu/MLGDevice.h
+++ b/gfx/layers/mlgpu/MLGDevice.h
@@ -413,16 +413,22 @@ public:
     return mMaxConstantBufferBindSize;
   }
 
 protected:
   virtual ~MLGDevice();
 
   virtual void SetPrimitiveTopology(MLGPrimitiveTopology aTopology) = 0;
 
+  // Optionally run a runtime test to determine if constant buffer offset
+  // binding works.
+  virtual bool VerifyConstantBufferOffsetting() {
+    return true;
+  }
+
   // Used during initialization to record failure reasons.
   bool Fail(const nsCString& aFailureId, const nsCString* aMessage);
 
   // Used during initialization to record failure reasons. Note: our
   // MOZ_FORMAT_PRINTF macro does not work on this function, so we
   // disable the warning.
 #if defined(__GNUC__)
 # pragma GCC diagnostic push