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/Commands.h"
21 #include "dawn_native/Device.h"
22 #include "dawn_native/RenderBundle.h"
23 #include "dawn_native/RenderPipeline.h"
24 
25 #include <math.h>
26 #include <cstring>
27 
28 namespace dawn_native {
29 
30     // The usage tracker is passed in here, because it is prepopulated with usages from the
31     // BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the
32     // command, then this wouldn't be necessary.
RenderPassEncoder(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext,PassResourceUsageTracker usageTracker)33     RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
34                                          CommandEncoder* commandEncoder,
35                                          EncodingContext* encodingContext,
36                                          PassResourceUsageTracker usageTracker)
37         : RenderEncoderBase(device, encodingContext), mCommandEncoder(commandEncoder) {
38         mUsageTracker = std::move(usageTracker);
39     }
40 
RenderPassEncoder(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext,ErrorTag errorTag)41     RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
42                                          CommandEncoder* commandEncoder,
43                                          EncodingContext* encodingContext,
44                                          ErrorTag errorTag)
45         : RenderEncoderBase(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) {
46     }
47 
MakeError(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext)48     RenderPassEncoder* RenderPassEncoder::MakeError(DeviceBase* device,
49                                                     CommandEncoder* commandEncoder,
50                                                     EncodingContext* encodingContext) {
51         return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError);
52     }
53 
EndPass()54     void RenderPassEncoder::EndPass() {
55         if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
56                 allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
57 
58                 return {};
59             })) {
60             mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage());
61         }
62     }
63 
SetStencilReference(uint32_t reference)64     void RenderPassEncoder::SetStencilReference(uint32_t reference) {
65         mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
66             SetStencilReferenceCmd* cmd =
67                 allocator->Allocate<SetStencilReferenceCmd>(Command::SetStencilReference);
68             cmd->reference = reference;
69 
70             return {};
71         });
72     }
73 
SetBlendColor(const Color * color)74     void RenderPassEncoder::SetBlendColor(const Color* color) {
75         mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
76             SetBlendColorCmd* cmd = allocator->Allocate<SetBlendColorCmd>(Command::SetBlendColor);
77             cmd->color = *color;
78 
79             return {};
80         });
81     }
82 
SetViewport(float x,float y,float width,float height,float minDepth,float maxDepth)83     void RenderPassEncoder::SetViewport(float x,
84                                         float y,
85                                         float width,
86                                         float height,
87                                         float minDepth,
88                                         float maxDepth) {
89         mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
90             if ((isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) ||
91                  isnan(maxDepth))) {
92                 return DAWN_VALIDATION_ERROR("NaN is not allowed.");
93             }
94 
95             // TODO(yunchao.he@intel.com): there are more restrictions for x, y, width and height in
96             // Vulkan, and height can be a negative value in Vulkan 1.1. Revisit this part later
97             // (say, for WebGPU v1).
98             if (width <= 0 || height <= 0) {
99                 return DAWN_VALIDATION_ERROR("Width and height must be greater than 0.");
100             }
101 
102             if (minDepth < 0 || minDepth > 1 || maxDepth < 0 || maxDepth > 1) {
103                 return DAWN_VALIDATION_ERROR("minDepth and maxDepth must be in [0, 1].");
104             }
105 
106             SetViewportCmd* cmd = allocator->Allocate<SetViewportCmd>(Command::SetViewport);
107             cmd->x = x;
108             cmd->y = y;
109             cmd->width = width;
110             cmd->height = height;
111             cmd->minDepth = minDepth;
112             cmd->maxDepth = maxDepth;
113 
114             return {};
115         });
116     }
117 
SetScissorRect(uint32_t x,uint32_t y,uint32_t width,uint32_t height)118     void RenderPassEncoder::SetScissorRect(uint32_t x,
119                                            uint32_t y,
120                                            uint32_t width,
121                                            uint32_t height) {
122         mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
123             if (width == 0 || height == 0) {
124                 return DAWN_VALIDATION_ERROR("Width and height must be greater than 0.");
125             }
126 
127             SetScissorRectCmd* cmd =
128                 allocator->Allocate<SetScissorRectCmd>(Command::SetScissorRect);
129             cmd->x = x;
130             cmd->y = y;
131             cmd->width = width;
132             cmd->height = height;
133 
134             return {};
135         });
136     }
137 
ExecuteBundles(uint32_t count,RenderBundleBase * const * renderBundles)138     void RenderPassEncoder::ExecuteBundles(uint32_t count, RenderBundleBase* const* renderBundles) {
139         mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
140             for (uint32_t i = 0; i < count; ++i) {
141                 DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i]));
142             }
143 
144             ExecuteBundlesCmd* cmd =
145                 allocator->Allocate<ExecuteBundlesCmd>(Command::ExecuteBundles);
146             cmd->count = count;
147 
148             Ref<RenderBundleBase>* bundles = allocator->AllocateData<Ref<RenderBundleBase>>(count);
149             for (uint32_t i = 0; i < count; ++i) {
150                 bundles[i] = renderBundles[i];
151 
152                 const PassResourceUsage& usages = bundles[i]->GetResourceUsage();
153                 for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
154                     mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
155                 }
156                 for (uint32_t i = 0; i < usages.textures.size(); ++i) {
157                     mUsageTracker.TextureUsedAs(usages.textures[i], usages.textureUsages[i]);
158                 }
159             }
160 
161             return {};
162         });
163     }
164 
165 }  // namespace dawn_native
166