1 // Copyright 2017 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/opengl/ShaderModuleGL.h"
16 
17 #include "common/Assert.h"
18 #include "common/Platform.h"
19 #include "dawn_native/opengl/DeviceGL.h"
20 
21 #include <spirv_glsl.hpp>
22 
23 #include <sstream>
24 
25 namespace dawn_native { namespace opengl {
26 
GetBindingName(uint32_t group,uint32_t binding)27     std::string GetBindingName(uint32_t group, uint32_t binding) {
28         std::ostringstream o;
29         o << "dawn_binding_" << group << "_" << binding;
30         return o.str();
31     }
32 
operator <(const BindingLocation & a,const BindingLocation & b)33     bool operator<(const BindingLocation& a, const BindingLocation& b) {
34         return std::tie(a.group, a.binding) < std::tie(b.group, b.binding);
35     }
36 
operator <(const CombinedSampler & a,const CombinedSampler & b)37     bool operator<(const CombinedSampler& a, const CombinedSampler& b) {
38         return std::tie(a.samplerLocation, a.textureLocation) <
39                std::tie(b.samplerLocation, b.textureLocation);
40     }
41 
GetName() const42     std::string CombinedSampler::GetName() const {
43         std::ostringstream o;
44         o << "dawn_combined";
45         o << "_" << samplerLocation.group << "_" << samplerLocation.binding;
46         o << "_with_" << textureLocation.group << "_" << textureLocation.binding;
47         return o.str();
48     }
49 
50     // static
Create(Device * device,const ShaderModuleDescriptor * descriptor)51     ResultOrError<ShaderModule*> ShaderModule::Create(Device* device,
52                                                       const ShaderModuleDescriptor* descriptor) {
53         std::unique_ptr<ShaderModule> module(new ShaderModule(device, descriptor));
54         if (!module)
55             return DAWN_VALIDATION_ERROR("Unable to create ShaderModule");
56         DAWN_TRY(module->Initialize(descriptor));
57         return module.release();
58     }
59 
GetSource() const60     const char* ShaderModule::GetSource() const {
61         return mGlslSource.c_str();
62     }
63 
GetCombinedSamplerInfo() const64     const ShaderModule::CombinedSamplerInfo& ShaderModule::GetCombinedSamplerInfo() const {
65         return mCombinedInfo;
66     }
67 
ShaderModule(Device * device,const ShaderModuleDescriptor * descriptor)68     ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor)
69         : ShaderModuleBase(device, descriptor) {
70     }
71 
Initialize(const ShaderModuleDescriptor * descriptor)72     MaybeError ShaderModule::Initialize(const ShaderModuleDescriptor* descriptor) {
73         std::unique_ptr<spirv_cross::CompilerGLSL> compiler_impl;
74         spirv_cross::CompilerGLSL* compiler;
75 
76         if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
77             // If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to
78             // be updated.
79             shaderc_spvc::CompileOptions options = GetCompileOptions();
80 
81             // The range of Z-coordinate in the clipping volume of OpenGL is [-w, w], while it is
82             // [0, w] in D3D12, Metal and Vulkan, so we should normalize it in shaders in all
83             // backends. See the documentation of
84             // spirv_cross::CompilerGLSL::Options::vertex::fixup_clipspace for more details.
85             options.SetFlipVertY(true);
86             options.SetFixupClipspace(true);
87 
88             // TODO(cwallez@chromium.org): discover the backing context version and use that.
89 #if defined(DAWN_PLATFORM_APPLE)
90             options.SetGLSLLanguageVersion(410);
91 #else
92             options.SetGLSLLanguageVersion(440);
93 #endif
94             DAWN_TRY(CheckSpvcSuccess(
95                 mSpvcContext.InitializeForGlsl(descriptor->code, descriptor->codeSize, options),
96                 "Unable to initialize instance of spvc"));
97             DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetCompiler(reinterpret_cast<void**>(&compiler)),
98                                       "Unable to get cross compiler"));
99         } else {
100             // If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to
101             // be updated.
102             spirv_cross::CompilerGLSL::Options options;
103 
104             // The range of Z-coordinate in the clipping volume of OpenGL is [-w, w], while it is
105             // [0, w] in D3D12, Metal and Vulkan, so we should normalize it in shaders in all
106             // backends. See the documentation of
107             // spirv_cross::CompilerGLSL::Options::vertex::fixup_clipspace for more details.
108             options.vertex.flip_vert_y = true;
109             options.vertex.fixup_clipspace = true;
110 
111             // TODO(cwallez@chromium.org): discover the backing context version and use that.
112 #if defined(DAWN_PLATFORM_APPLE)
113         options.version = 410;
114 #else
115         options.version = 440;
116 #endif
117 
118         compiler_impl =
119             std::make_unique<spirv_cross::CompilerGLSL>(descriptor->code, descriptor->codeSize);
120         compiler = compiler_impl.get();
121         compiler->set_common_options(options);
122         }
123 
124         DAWN_TRY(ExtractSpirvInfo(*compiler));
125 
126         const ShaderModuleBase::ModuleBindingInfo& bindingInfo = GetBindingInfo();
127 
128         // Extract bindings names so that it can be used to get its location in program.
129         // Now translate the separate sampler / textures into combined ones and store their info.
130         // We need to do this before removing the set and binding decorations.
131         if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
132             mSpvcContext.BuildCombinedImageSamplers();
133         } else {
134             compiler->build_combined_image_samplers();
135         }
136 
137         if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
138             std::vector<shaderc_spvc_combined_image_sampler> samplers;
139             mSpvcContext.GetCombinedImageSamplers(&samplers);
140             for (auto sampler : samplers) {
141                 mCombinedInfo.emplace_back();
142                 auto& info = mCombinedInfo.back();
143 
144                 mSpvcContext.GetDecoration(sampler.sampler_id,
145                                            shaderc_spvc_decoration_descriptorset,
146                                            &info.samplerLocation.group);
147                 mSpvcContext.GetDecoration(sampler.sampler_id, shaderc_spvc_decoration_binding,
148                                            &info.samplerLocation.binding);
149                 mSpvcContext.GetDecoration(sampler.image_id, shaderc_spvc_decoration_descriptorset,
150                                            &info.textureLocation.group);
151                 mSpvcContext.GetDecoration(sampler.image_id, shaderc_spvc_decoration_binding,
152                                            &info.textureLocation.binding);
153                 mSpvcContext.SetName(sampler.combined_id, info.GetName());
154             }
155         } else {
156             for (const auto& combined : compiler->get_combined_image_samplers()) {
157                 mCombinedInfo.emplace_back();
158 
159                 auto& info = mCombinedInfo.back();
160                 info.samplerLocation.group =
161                     compiler->get_decoration(combined.sampler_id, spv::DecorationDescriptorSet);
162                 info.samplerLocation.binding =
163                     compiler->get_decoration(combined.sampler_id, spv::DecorationBinding);
164                 info.textureLocation.group =
165                     compiler->get_decoration(combined.image_id, spv::DecorationDescriptorSet);
166                 info.textureLocation.binding =
167                     compiler->get_decoration(combined.image_id, spv::DecorationBinding);
168                 compiler->set_name(combined.combined_id, info.GetName());
169             }
170         }
171 
172         // Change binding names to be "dawn_binding_<group>_<binding>".
173         // Also unsets the SPIRV "Binding" decoration as it outputs "layout(binding=)" which
174         // isn't supported on OSX's OpenGL.
175         for (uint32_t group = 0; group < kMaxBindGroups; ++group) {
176             for (const auto& it : bindingInfo[group]) {
177                 BindingNumber bindingNumber = it.first;
178                 const auto& info = it.second;
179 
180                 if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
181                     mSpvcContext.SetName(info.base_type_id, GetBindingName(group, bindingNumber));
182                     mSpvcContext.UnsetDecoration(info.id, shaderc_spvc_decoration_binding);
183                     mSpvcContext.UnsetDecoration(info.id, shaderc_spvc_decoration_descriptorset);
184                 } else {
185                     compiler->set_name(info.base_type_id, GetBindingName(group, bindingNumber));
186                     compiler->unset_decoration(info.id, spv::DecorationBinding);
187                     compiler->unset_decoration(info.id, spv::DecorationDescriptorSet);
188                 }
189             }
190         }
191 
192         if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
193             shaderc_spvc::CompilationResult result;
194             DAWN_TRY(CheckSpvcSuccess(mSpvcContext.CompileShader(&result),
195                                       "Unable to compile GLSL shader using spvc"));
196             DAWN_TRY(CheckSpvcSuccess(result.GetStringOutput(&mGlslSource),
197                                       "Unable to get GLSL shader text"));
198         } else {
199             mGlslSource = compiler->compile();
200         }
201         return {};
202     }
203 
204 }}  // namespace dawn_native::opengl
205