1 // Copyright 2018 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/RenderPassEncoder.h" 16 17 #include "common/Constants.h" 18 #include "dawn_native/Buffer.h" 19 #include "dawn_native/CommandEncoder.h" 20 #include "dawn_native/CommandValidation.h" 21 #include "dawn_native/Commands.h" 22 #include "dawn_native/Device.h" 23 #include "dawn_native/ObjectType_autogen.h" 24 #include "dawn_native/QuerySet.h" 25 #include "dawn_native/RenderBundle.h" 26 #include "dawn_native/RenderPipeline.h" 27 28 #include <math.h> 29 #include <cstring> 30 31 namespace dawn_native { 32 namespace { 33 34 // Check the query at queryIndex is unavailable, otherwise it cannot be written. ValidateQueryIndexOverwrite(QuerySetBase * querySet,uint32_t queryIndex,const QueryAvailabilityMap & queryAvailabilityMap)35 MaybeError ValidateQueryIndexOverwrite(QuerySetBase* querySet, 36 uint32_t queryIndex, 37 const QueryAvailabilityMap& queryAvailabilityMap) { 38 auto it = queryAvailabilityMap.find(querySet); 39 DAWN_INVALID_IF(it != queryAvailabilityMap.end() && it->second[queryIndex], 40 "Query index %u of %s is written to twice in a render pass.", 41 queryIndex, querySet); 42 43 return {}; 44 } 45 46 } // namespace 47 48 // The usage tracker is passed in here, because it is prepopulated with usages from the 49 // BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the 50 // command, then this wouldn't be necessary. RenderPassEncoder(DeviceBase * device,const RenderPassDescriptor * descriptor,CommandEncoder * commandEncoder,EncodingContext * encodingContext,RenderPassResourceUsageTracker usageTracker,Ref<AttachmentState> attachmentState,QuerySetBase * occlusionQuerySet,uint32_t renderTargetWidth,uint32_t renderTargetHeight,bool depthReadOnly,bool stencilReadOnly)51 RenderPassEncoder::RenderPassEncoder(DeviceBase* device, 52 const RenderPassDescriptor* descriptor, 53 CommandEncoder* commandEncoder, 54 EncodingContext* encodingContext, 55 RenderPassResourceUsageTracker usageTracker, 56 Ref<AttachmentState> attachmentState, 57 QuerySetBase* occlusionQuerySet, 58 uint32_t renderTargetWidth, 59 uint32_t renderTargetHeight, 60 bool depthReadOnly, 61 bool stencilReadOnly) 62 : RenderEncoderBase(device, 63 descriptor->label, 64 encodingContext, 65 std::move(attachmentState), 66 depthReadOnly, 67 stencilReadOnly), 68 mCommandEncoder(commandEncoder), 69 mRenderTargetWidth(renderTargetWidth), 70 mRenderTargetHeight(renderTargetHeight), 71 mOcclusionQuerySet(occlusionQuerySet) { 72 mUsageTracker = std::move(usageTracker); 73 TrackInDevice(); 74 } 75 RenderPassEncoder(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext,ErrorTag errorTag)76 RenderPassEncoder::RenderPassEncoder(DeviceBase* device, 77 CommandEncoder* commandEncoder, 78 EncodingContext* encodingContext, 79 ErrorTag errorTag) 80 : RenderEncoderBase(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) { 81 } 82 MakeError(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext)83 RenderPassEncoder* RenderPassEncoder::MakeError(DeviceBase* device, 84 CommandEncoder* commandEncoder, 85 EncodingContext* encodingContext) { 86 return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError); 87 } 88 DestroyImpl()89 void RenderPassEncoder::DestroyImpl() { 90 RenderEncoderBase::DestroyImpl(); 91 // Ensure that the pass has exited. This is done for passes only since validation requires 92 // they exit before destruction while bundles do not. 93 mEncodingContext->EnsurePassExited(this); 94 } 95 GetType() const96 ObjectType RenderPassEncoder::GetType() const { 97 return ObjectType::RenderPassEncoder; 98 } 99 TrackQueryAvailability(QuerySetBase * querySet,uint32_t queryIndex)100 void RenderPassEncoder::TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex) { 101 DAWN_ASSERT(querySet != nullptr); 102 103 // Track the query availability with true on render pass for rewrite validation and query 104 // reset on render pass on Vulkan 105 mUsageTracker.TrackQueryAvailability(querySet, queryIndex); 106 107 // Track it again on command encoder for zero-initializing when resolving unused queries. 108 mCommandEncoder->TrackQueryAvailability(querySet, queryIndex); 109 } 110 APIEndPass()111 void RenderPassEncoder::APIEndPass() { 112 if (mEncodingContext->TryEncode( 113 this, 114 [&](CommandAllocator* allocator) -> MaybeError { 115 if (IsValidationEnabled()) { 116 DAWN_TRY(ValidateProgrammableEncoderEnd()); 117 118 DAWN_INVALID_IF( 119 mOcclusionQueryActive, 120 "Render pass %s ended with incomplete occlusion query index %u of %s.", 121 this, mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get()); 122 } 123 124 allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass); 125 DAWN_TRY(mEncodingContext->ExitRenderPass(this, std::move(mUsageTracker), 126 mCommandEncoder.Get(), 127 std::move(mIndirectDrawMetadata))); 128 return {}; 129 }, 130 "encoding %s.EndPass().", this)) { 131 } 132 } 133 APISetStencilReference(uint32_t reference)134 void RenderPassEncoder::APISetStencilReference(uint32_t reference) { 135 mEncodingContext->TryEncode( 136 this, 137 [&](CommandAllocator* allocator) -> MaybeError { 138 SetStencilReferenceCmd* cmd = 139 allocator->Allocate<SetStencilReferenceCmd>(Command::SetStencilReference); 140 cmd->reference = reference; 141 142 return {}; 143 }, 144 "encoding %s.SetStencilReference(%u).", this, reference); 145 } 146 APISetBlendConstant(const Color * color)147 void RenderPassEncoder::APISetBlendConstant(const Color* color) { 148 mEncodingContext->TryEncode( 149 this, 150 [&](CommandAllocator* allocator) -> MaybeError { 151 SetBlendConstantCmd* cmd = 152 allocator->Allocate<SetBlendConstantCmd>(Command::SetBlendConstant); 153 cmd->color = *color; 154 155 return {}; 156 }, 157 "encoding %s.SetBlendConstant(%s).", this, color); 158 } 159 APISetViewport(float x,float y,float width,float height,float minDepth,float maxDepth)160 void RenderPassEncoder::APISetViewport(float x, 161 float y, 162 float width, 163 float height, 164 float minDepth, 165 float maxDepth) { 166 mEncodingContext->TryEncode( 167 this, 168 [&](CommandAllocator* allocator) -> MaybeError { 169 if (IsValidationEnabled()) { 170 DAWN_INVALID_IF( 171 (isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) || 172 isnan(maxDepth)), 173 "A parameter of the viewport (x: %f, y: %f, width: %f, height: %f, " 174 "minDepth: %f, maxDepth: %f) is NaN.", 175 x, y, width, height, minDepth, maxDepth); 176 177 DAWN_INVALID_IF( 178 x < 0 || y < 0 || width < 0 || height < 0, 179 "Viewport bounds (x: %f, y: %f, width: %f, height: %f) contains a negative " 180 "value.", 181 x, y, width, height); 182 183 DAWN_INVALID_IF( 184 x + width > mRenderTargetWidth || y + height > mRenderTargetHeight, 185 "Viewport bounds (x: %f, y: %f, width: %f, height: %f) are not contained " 186 "in " 187 "the render target dimensions (%u x %u).", 188 x, y, width, height, mRenderTargetWidth, mRenderTargetHeight); 189 190 // Check for depths being in [0, 1] and min <= max in 3 checks instead of 5. 191 DAWN_INVALID_IF(minDepth < 0 || minDepth > maxDepth || maxDepth > 1, 192 "Viewport minDepth (%f) and maxDepth (%f) are not in [0, 1] or " 193 "minDepth was " 194 "greater than maxDepth.", 195 minDepth, maxDepth); 196 } 197 198 SetViewportCmd* cmd = allocator->Allocate<SetViewportCmd>(Command::SetViewport); 199 cmd->x = x; 200 cmd->y = y; 201 cmd->width = width; 202 cmd->height = height; 203 cmd->minDepth = minDepth; 204 cmd->maxDepth = maxDepth; 205 206 return {}; 207 }, 208 "encoding %s.SetViewport(%f, %f, %f, %f, %f, %f).", this, x, y, width, height, minDepth, 209 maxDepth); 210 } 211 APISetScissorRect(uint32_t x,uint32_t y,uint32_t width,uint32_t height)212 void RenderPassEncoder::APISetScissorRect(uint32_t x, 213 uint32_t y, 214 uint32_t width, 215 uint32_t height) { 216 mEncodingContext->TryEncode( 217 this, 218 [&](CommandAllocator* allocator) -> MaybeError { 219 if (IsValidationEnabled()) { 220 DAWN_INVALID_IF( 221 width > mRenderTargetWidth || height > mRenderTargetHeight || 222 x > mRenderTargetWidth - width || y > mRenderTargetHeight - height, 223 "Scissor rect (x: %u, y: %u, width: %u, height: %u) is not contained in " 224 "the render target dimensions (%u x %u).", 225 x, y, width, height, mRenderTargetWidth, mRenderTargetHeight); 226 } 227 228 SetScissorRectCmd* cmd = 229 allocator->Allocate<SetScissorRectCmd>(Command::SetScissorRect); 230 cmd->x = x; 231 cmd->y = y; 232 cmd->width = width; 233 cmd->height = height; 234 235 return {}; 236 }, 237 "encoding %s.SetScissorRect(%u, %u, %u, %u).", this, x, y, width, height); 238 } 239 APIExecuteBundles(uint32_t count,RenderBundleBase * const * renderBundles)240 void RenderPassEncoder::APIExecuteBundles(uint32_t count, 241 RenderBundleBase* const* renderBundles) { 242 mEncodingContext->TryEncode( 243 this, 244 [&](CommandAllocator* allocator) -> MaybeError { 245 if (IsValidationEnabled()) { 246 const AttachmentState* attachmentState = GetAttachmentState(); 247 bool depthReadOnlyInPass = IsDepthReadOnly(); 248 bool stencilReadOnlyInPass = IsStencilReadOnly(); 249 for (uint32_t i = 0; i < count; ++i) { 250 DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i])); 251 252 // TODO(dawn:563): Give more detail about why the states are incompatible. 253 DAWN_INVALID_IF( 254 attachmentState != renderBundles[i]->GetAttachmentState(), 255 "Attachment state of renderBundles[%i] (%s) is not compatible with " 256 "attachment state of %s.", 257 i, renderBundles[i], this); 258 259 bool depthReadOnlyInBundle = renderBundles[i]->IsDepthReadOnly(); 260 DAWN_INVALID_IF( 261 depthReadOnlyInPass && !depthReadOnlyInBundle, 262 "DepthReadOnly (%u) of renderBundle[%i] (%s) is not compatible " 263 "with DepthReadOnly (%u) of %s.", 264 depthReadOnlyInBundle, i, renderBundles[i], depthReadOnlyInPass, this); 265 266 bool stencilReadOnlyInBundle = renderBundles[i]->IsStencilReadOnly(); 267 DAWN_INVALID_IF(stencilReadOnlyInPass && !stencilReadOnlyInBundle, 268 "StencilReadOnly (%u) of renderBundle[%i] (%s) is not " 269 "compatible with StencilReadOnly (%u) of %s.", 270 stencilReadOnlyInBundle, i, renderBundles[i], 271 stencilReadOnlyInPass, this); 272 } 273 } 274 275 mCommandBufferState = CommandBufferStateTracker{}; 276 277 ExecuteBundlesCmd* cmd = 278 allocator->Allocate<ExecuteBundlesCmd>(Command::ExecuteBundles); 279 cmd->count = count; 280 281 Ref<RenderBundleBase>* bundles = 282 allocator->AllocateData<Ref<RenderBundleBase>>(count); 283 for (uint32_t i = 0; i < count; ++i) { 284 bundles[i] = renderBundles[i]; 285 286 const RenderPassResourceUsage& usages = bundles[i]->GetResourceUsage(); 287 for (uint32_t i = 0; i < usages.buffers.size(); ++i) { 288 mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]); 289 } 290 291 for (uint32_t i = 0; i < usages.textures.size(); ++i) { 292 mUsageTracker.AddRenderBundleTextureUsage(usages.textures[i], 293 usages.textureUsages[i]); 294 } 295 296 if (IsValidationEnabled()) { 297 mIndirectDrawMetadata.AddBundle(renderBundles[i]); 298 } 299 } 300 301 return {}; 302 }, 303 "encoding %s.ExecuteBundles(%u, ...).", this, count); 304 } 305 APIBeginOcclusionQuery(uint32_t queryIndex)306 void RenderPassEncoder::APIBeginOcclusionQuery(uint32_t queryIndex) { 307 mEncodingContext->TryEncode( 308 this, 309 [&](CommandAllocator* allocator) -> MaybeError { 310 if (IsValidationEnabled()) { 311 DAWN_INVALID_IF(mOcclusionQuerySet.Get() == nullptr, 312 "The occlusionQuerySet in RenderPassDescriptor is not set."); 313 314 // The type of querySet has been validated by ValidateRenderPassDescriptor 315 316 DAWN_INVALID_IF(queryIndex >= mOcclusionQuerySet->GetQueryCount(), 317 "Query index (%u) exceeds the number of queries (%u) in %s.", 318 queryIndex, mOcclusionQuerySet->GetQueryCount(), 319 mOcclusionQuerySet.Get()); 320 321 DAWN_INVALID_IF(mOcclusionQueryActive, 322 "An occlusion query (%u) in %s is already active.", 323 mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get()); 324 325 DAWN_TRY_CONTEXT( 326 ValidateQueryIndexOverwrite(mOcclusionQuerySet.Get(), queryIndex, 327 mUsageTracker.GetQueryAvailabilityMap()), 328 "validating the occlusion query index (%u) in %s", queryIndex, 329 mOcclusionQuerySet.Get()); 330 } 331 332 // Record the current query index for endOcclusionQuery. 333 mCurrentOcclusionQueryIndex = queryIndex; 334 mOcclusionQueryActive = true; 335 336 BeginOcclusionQueryCmd* cmd = 337 allocator->Allocate<BeginOcclusionQueryCmd>(Command::BeginOcclusionQuery); 338 cmd->querySet = mOcclusionQuerySet.Get(); 339 cmd->queryIndex = queryIndex; 340 341 return {}; 342 }, 343 "encoding %s.BeginOcclusionQuery(%u).", this, queryIndex); 344 } 345 APIEndOcclusionQuery()346 void RenderPassEncoder::APIEndOcclusionQuery() { 347 mEncodingContext->TryEncode( 348 this, 349 [&](CommandAllocator* allocator) -> MaybeError { 350 if (IsValidationEnabled()) { 351 DAWN_INVALID_IF(!mOcclusionQueryActive, "No occlusion queries are active."); 352 } 353 354 TrackQueryAvailability(mOcclusionQuerySet.Get(), mCurrentOcclusionQueryIndex); 355 356 mOcclusionQueryActive = false; 357 358 EndOcclusionQueryCmd* cmd = 359 allocator->Allocate<EndOcclusionQueryCmd>(Command::EndOcclusionQuery); 360 cmd->querySet = mOcclusionQuerySet.Get(); 361 cmd->queryIndex = mCurrentOcclusionQueryIndex; 362 363 return {}; 364 }, 365 "encoding %s.EndOcclusionQuery().", this); 366 } 367 APIWriteTimestamp(QuerySetBase * querySet,uint32_t queryIndex)368 void RenderPassEncoder::APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) { 369 mEncodingContext->TryEncode( 370 this, 371 [&](CommandAllocator* allocator) -> MaybeError { 372 if (IsValidationEnabled()) { 373 DAWN_TRY(GetDevice()->ValidateObject(querySet)); 374 DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); 375 DAWN_TRY_CONTEXT( 376 ValidateQueryIndexOverwrite(querySet, queryIndex, 377 mUsageTracker.GetQueryAvailabilityMap()), 378 "validating the timestamp query index (%u) of %s", queryIndex, querySet); 379 } 380 381 TrackQueryAvailability(querySet, queryIndex); 382 383 WriteTimestampCmd* cmd = 384 allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp); 385 cmd->querySet = querySet; 386 cmd->queryIndex = queryIndex; 387 388 return {}; 389 }, 390 "encoding %s.WriteTimestamp(%s, %u).", this, querySet, queryIndex); 391 } 392 393 } // namespace dawn_native 394