Merge pull request #1071 from cdavis5e/render-area-size-min

Ensure there is at least one pixel in the render area.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm
index e657b04..bd63403 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm
@@ -145,6 +145,9 @@
                                          atIndex: pipeline->getOutputBufferIndex().stages[kMVKShaderStageVertex]];
                 }
 				[mtlTessCtlEncoder setStageInRegion: MTLRegionMake2D(_firstVertex, _firstInstance, _vertexCount, _instanceCount)];
+				// If there are vertex bindings with a zero vertex divisor, I need to offset them by
+				// _firstInstance * stride, since that is the expected behaviour for a divisor of 0.
+                cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
 				id<MTLComputePipelineState> vtxState = pipeline->getTessVertexStageState();
 				if (cmdEncoder->getDevice()->_pMetalFeatures->nonUniformThreadgroups) {
 #if MVK_MACOS_OR_IOS
@@ -245,6 +248,7 @@
                     MVKRenderSubpass* subpass = cmdEncoder->getSubpass();
                     uint32_t viewCount = subpass->isMultiview() ? subpass->getViewCountInMetalPass(cmdEncoder->getMultiviewPassIndex()) : 1;
                     uint32_t instanceCount = _instanceCount * viewCount;
+                    cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
                     if (cmdEncoder->_pDeviceMetalFeatures->baseVertexInstanceDrawing) {
                         [cmdEncoder->_mtlRenderEncoder drawPrimitives: cmdEncoder->_mtlPrimitiveType
                                                           vertexStart: _firstVertex
@@ -341,6 +345,9 @@
                                       offset: idxBuffOffset
                                      atIndex: pipeline->getIndirectParamsIndex().stages[kMVKShaderStageVertex]];
 				[mtlTessCtlEncoder setStageInRegion: MTLRegionMake2D(_vertexOffset, _firstInstance, _indexCount, _instanceCount)];
+				// If there are vertex bindings with a zero vertex divisor, I need to offset them by
+				// _firstInstance * stride, since that is the expected behaviour for a divisor of 0.
+                cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
 				id<MTLComputePipelineState> vtxState = ibb.mtlIndexType == MTLIndexTypeUInt16 ? pipeline->getTessVertexStageIndex16State() : pipeline->getTessVertexStageIndex32State();
 				if (cmdEncoder->getDevice()->_pMetalFeatures->nonUniformThreadgroups) {
 #if MVK_MACOS_OR_IOS
@@ -444,6 +451,7 @@
                     MVKRenderSubpass* subpass = cmdEncoder->getSubpass();
                     uint32_t viewCount = subpass->isMultiview() ? subpass->getViewCountInMetalPass(cmdEncoder->getMultiviewPassIndex()) : 1;
                     uint32_t instanceCount = _instanceCount * viewCount;
+                    cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
                     if (cmdEncoder->_pDeviceMetalFeatures->baseVertexInstanceDrawing) {
                         [cmdEncoder->_mtlRenderEncoder drawIndexedPrimitives: cmdEncoder->_mtlPrimitiveType
                                                                   indexCount: _indexCount
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index 0c6cd71..62d4a8a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -25,6 +25,7 @@
 #include <unordered_map>
 
 class MVKCommandEncoder;
+class MVKGraphicsPipeline;
 class MVKOcclusionQueryPool;
 
 struct MVKShaderImplicitRezBinding;
@@ -508,6 +509,9 @@
                         std::function<void(MVKCommandEncoder*, MVKMTLTextureBinding&)> bindTexture,
                         std::function<void(MVKCommandEncoder*, MVKMTLSamplerStateBinding&)> bindSampler);
 
+	/** Offset all buffers for vertex attribute bindings with zero divisors by the given number of strides. */
+	void offsetZeroDivisorVertexBuffers(MVKGraphicsStage stage, MVKGraphicsPipeline* pipeline, uint32_t firstInstance);
+
 #pragma mark Construction
     
     /** Constructs this instance for the specified command encoder. */
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index 450ccaf..671ed45 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -610,6 +610,30 @@
     encodeBinding<MVKMTLSamplerStateBinding>(shaderStage.samplerStateBindings, shaderStage.areSamplerStateBindingsDirty, bindSampler);
 }
 
+void MVKGraphicsResourcesCommandEncoderState::offsetZeroDivisorVertexBuffers(MVKGraphicsStage stage,
+                                                                             MVKGraphicsPipeline* pipeline,
+                                                                             uint32_t firstInstance) {
+    auto& shaderStage = _shaderStageResourceBindings[kMVKShaderStageVertex];
+    for (auto& binding : pipeline->getZeroDivisorVertexBindings()) {
+        uint32_t mtlBuffIdx = pipeline->getMetalBufferIndexForVertexAttributeBinding(binding.first);
+        auto iter = std::find_if(shaderStage.bufferBindings.begin(), shaderStage.bufferBindings.end(), [mtlBuffIdx](const MVKMTLBufferBinding& b) { return b.index == mtlBuffIdx; });
+		if (!iter) { continue; }
+        switch (stage) {
+            case kMVKGraphicsStageVertex:
+                [_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBufferOffset: iter->offset + firstInstance * binding.second
+                                                                                                    atIndex: mtlBuffIdx];
+                break;
+            case kMVKGraphicsStageRasterization:
+                [_cmdEncoder->_mtlRenderEncoder setVertexBufferOffset: iter->offset + firstInstance * binding.second
+                                                              atIndex: mtlBuffIdx];
+                break;
+            default:
+                assert(false);      // If we hit this, something went wrong.
+                break;
+        }
+    }
+}
+
 // Mark everything as dirty
 void MVKGraphicsResourcesCommandEncoderState::markDirty() {
     MVKCommandEncoderState::markDirty();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 61ebd09..32030bb 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -3368,9 +3368,9 @@
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR: {
 				auto* requestedFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesKHR*)next;
-				enableFeatures(&_enabledPortabilityFeatures.triangleFans,
-							   &requestedFeatures->triangleFans,
-							   &pdPortabilityFeatures.triangleFans, 15);
+				enableFeatures(&_enabledPortabilityFeatures.constantAlphaColorBlendFactors,
+							   &requestedFeatures->constantAlphaColorBlendFactors,
+							   &pdPortabilityFeatures.constantAlphaColorBlendFactors, 15);
 				break;
 			}
 			default:
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index 7c53e82..8813a5b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -195,6 +195,9 @@
 	uint32_t translationOffset;
 };
 
+/** Describes a vertex buffer binding whose divisor is zero. */
+typedef std::pair<uint32_t, uint32_t> MVKZeroDivisorVertexBinding;
+
 typedef MVKSmallVector<MVKGraphicsStage, 4> MVKPiplineStages;
 
 /** The number of dynamic states possible in Vulkan. */
@@ -259,6 +262,9 @@
 	/** Returns the collection of translated vertex bindings. */
 	MVKArrayRef<MVKTranslatedVertexBinding> getTranslatedVertexBindings() { return _translatedVertexBindings.contents(); }
 
+	/** Returns the collection of instance-rate vertex bindings whose divisor is zero, along with their strides. */
+	MVKArrayRef<MVKZeroDivisorVertexBinding> getZeroDivisorVertexBindings() { return _zeroDivisorVertexBindings.contents(); }
+
 	/** Constructs an instance for the device and parent (which may be NULL). */
 	MVKGraphicsPipeline(MVKDevice* device,
 						MVKPipelineCache* pipelineCache,
@@ -306,6 +312,7 @@
 	MVKSmallVector<VkViewport, kMVKCachedViewportScissorCount> _viewports;
 	MVKSmallVector<VkRect2D, kMVKCachedViewportScissorCount> _scissors;
 	MVKSmallVector<MVKTranslatedVertexBinding> _translatedVertexBindings;
+	MVKSmallVector<MVKZeroDivisorVertexBinding> _zeroDivisorVertexBindings;
 
 	MTLComputePipelineDescriptor* _mtlTessVertexStageDesc = nil;
 	id<MTLFunction> _mtlTessVertexFunctions[3] = {nil, nil, nil};
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 4f59111..1a42059 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -1135,6 +1135,7 @@
     }
 
     // Vertex buffer divisors (step rates)
+    std::unordered_set<uint32_t> zeroDivisorBindings;
     if (pVertexInputDivisorState) {
         vbCnt = pVertexInputDivisorState->vertexBindingDivisorCount;
         for (uint32_t i = 0; i < vbCnt; i++) {
@@ -1143,8 +1144,10 @@
                 uint32_t vbIdx = getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding);
                 if ((NSUInteger)inputDesc.layouts[vbIdx].stepFunction == MTLStepFunctionPerInstance ||
 					(NSUInteger)inputDesc.layouts[vbIdx].stepFunction == MTLStepFunctionThreadPositionInGridY) {
-                    if (pVKVB->divisor == 0)
+                    if (pVKVB->divisor == 0) {
                         inputDesc.layouts[vbIdx].stepFunction = (decltype(inputDesc.layouts[vbIdx].stepFunction))MTLStepFunctionConstant;
+                        zeroDivisorBindings.insert(pVKVB->binding);
+                    }
                     inputDesc.layouts[vbIdx].stepRate = pVKVB->divisor;
                 }
             }
@@ -1185,6 +1188,9 @@
 							vaOffset = 0;
 						}
 						vaBinding = getTranslatedVertexBinding(vaBinding, origOffset - vaOffset, maxBinding);
+                        if (zeroDivisorBindings.count(pVKVB->binding)) {
+                            zeroDivisorBindings.insert(vaBinding);
+                        }
 					}
 					break;
 				}
@@ -1224,6 +1230,13 @@
 		}
 	}
 
+    // Collect all bindings with zero divisors. We need to remember them so we can offset
+    // the vertex buffers during a draw.
+    for (uint32_t binding : zeroDivisorBindings) {
+        uint32_t stride = (uint32_t)inputDesc.layouts[getMetalBufferIndexForVertexAttributeBinding(binding)].stride;
+        _zeroDivisorVertexBindings.emplace_back(binding, stride);
+    }
+
 	return true;
 }
 
diff --git a/MoltenVK/icd/MoltenVK_icd.json b/MoltenVK/icd/MoltenVK_icd.json
index 6059330..1b1685d 100644
--- a/MoltenVK/icd/MoltenVK_icd.json
+++ b/MoltenVK/icd/MoltenVK_icd.json
@@ -2,6 +2,6 @@
     "file_format_version" : "1.0.0",
     "ICD": {
         "library_path": "./libMoltenVK.dylib",
-        "api_version" : "1.0.0"
+        "api_version" : "1.1.0"
     }
 }