1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
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 "shader.h"
16
17 #include <algorithm>
18 #include <mutex>
19 #include <scene/ext/util.h>
20
21 #include <3d/render/default_material_constants.h>
22 #include <render/device/intf_gpu_resource_manager.h>
23
24 #include "util.h"
25
SCENE_BEGIN_NAMESPACE()26 SCENE_BEGIN_NAMESPACE()
27
28 bool GraphicsState::SetGraphicsState(RENDER_NS::RenderHandleReference handle)
29 {
30 std::unique_lock lock { mutex_ };
31 graphicsState_ = handle;
32 return true;
33 }
GetGraphicsState() const34 RENDER_NS::RenderHandleReference GraphicsState::GetGraphicsState() const
35 {
36 std::shared_lock lock { mutex_ };
37 return graphicsState_;
38 }
39
IsBlendEnabled(const RENDER_NS::GraphicsState & gs)40 static bool IsBlendEnabled(const RENDER_NS::GraphicsState& gs)
41 {
42 return std::any_of(gs.colorBlendState.colorAttachments,
43 gs.colorBlendState.colorAttachments + gs.colorBlendState.colorAttachmentCount,
44 [](const RENDER_NS::GraphicsState::ColorBlendState::Attachment& attachment) { return attachment.enableBlend; });
45 }
46
GetRenderSlot(const RENDER_NS::GraphicsState & gs)47 static BASE_NS::string_view GetRenderSlot(const RENDER_NS::GraphicsState& gs)
48 {
49 return IsBlendEnabled(gs) ? CORE3D_NS::DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT
50 : CORE3D_NS::DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE;
51 }
52
CreateGraphicsState(const IRenderContext::Ptr & context,RENDER_NS::RenderHandleReference state,BASE_NS::string_view defaultRenderSlot)53 RENDER_NS::GraphicsState GraphicsState::CreateGraphicsState(
54 const IRenderContext::Ptr& context, RENDER_NS::RenderHandleReference state, BASE_NS::string_view defaultRenderSlot)
55 {
56 auto& man = context->GetRenderer()->GetDevice().GetShaderManager();
57 RENDER_NS::GraphicsState gs;
58 RENDER_NS::IShaderManager::GraphicsStateVariantCreateInfo vinfo;
59 if (state) {
60 gs = man.GetGraphicsState(state);
61 auto id = man.GetRenderSlotId(state);
62 vinfo.renderSlot = man.GetRenderSlotName(id);
63 } else {
64 auto renderSlotId = man.GetRenderSlotId(defaultRenderSlot);
65 auto rsd = man.GetRenderSlotData(renderSlotId);
66 gs = man.GetGraphicsState(rsd.graphicsState);
67 vinfo.renderSlot = GetRenderSlot(gs);
68 }
69 std::unique_lock lock { mutex_ };
70 graphicsState_ =
71 man.CreateGraphicsState({ "SceneGS_" + BASE_NS::string(BASE_NS::to_string(uintptr_t(this))), gs }, vinfo);
72 return gs;
73 }
74
UpdateGraphicsState(const IRenderContext::Ptr & context,const RENDER_NS::GraphicsState & gs,BASE_NS::string_view renderSlot={})75 bool GraphicsState::UpdateGraphicsState(
76 const IRenderContext::Ptr& context, const RENDER_NS::GraphicsState& gs, BASE_NS::string_view renderSlot = {})
77 {
78 auto& man = context->GetRenderer()->GetDevice().GetShaderManager();
79 RENDER_NS::IShaderManager::GraphicsStateVariantCreateInfo vinfo { renderSlot };
80 if (renderSlot.empty()) {
81 vinfo.renderSlot = GetRenderSlot(gs);
82 }
83 auto s = man.CreateGraphicsState({ "SceneGS_" + BASE_NS::string(BASE_NS::to_string(uintptr_t(this))), gs }, vinfo);
84 {
85 bool update = false;
86 {
87 std::unique_lock lock { mutex_ };
88 update = s.GetHandle() != graphicsState_.GetHandle();
89 if (update) {
90 graphicsState_ = s;
91 }
92 }
93 if (update) {
94 if (auto ev = EventOnResourceChanged(META_NS::MetadataQuery::EXISTING)) {
95 META_NS::Invoke<META_NS::IOnChanged>(ev);
96 }
97 }
98 }
99 return true;
100 }
101
CreateNewGraphicsState(const IRenderContext::Ptr & context,bool blend)102 RENDER_NS::GraphicsState GraphicsState::CreateNewGraphicsState(const IRenderContext::Ptr& context, bool blend)
103 {
104 // notice: this is wrong for user shaders, fix later
105 auto& man = context->GetRenderer()->GetDevice().GetShaderManager();
106 auto oldgs = GetGraphicsState(context);
107 auto renderSlotId =
108 man.GetRenderSlotId(blend ? CORE3D_NS::DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT
109 : CORE3D_NS::DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE);
110 auto rsd = man.GetRenderSlotData(renderSlotId);
111 auto gs = man.GetGraphicsState(rsd.graphicsState);
112 gs.inputAssembly = oldgs.inputAssembly;
113 gs.depthStencilState = oldgs.depthStencilState;
114 gs.rasterizationState = oldgs.rasterizationState;
115 return gs;
116 }
117
GetGraphicsState(const IRenderContext::Ptr & context) const118 RENDER_NS::GraphicsState GraphicsState::GetGraphicsState(const IRenderContext::Ptr& context) const
119 {
120 auto& man = context->GetRenderer()->GetDevice().GetShaderManager();
121 std::shared_lock lock { mutex_ };
122 return man.GetGraphicsState(graphicsState_);
123 }
124
GetName() const125 BASE_NS::string Shader::GetName() const
126 {
127 return META_NS::GetValue(Name());
128 }
129
SetRenderHandle(RENDER_NS::RenderHandleReference handle)130 bool Shader::SetRenderHandle(RENDER_NS::RenderHandleReference handle)
131 {
132 return SetShaderState(handle, {});
133 }
134
SetShaderState(RENDER_NS::RenderHandleReference shader,RENDER_NS::RenderHandleReference graphics)135 bool Shader::SetShaderState(RENDER_NS::RenderHandleReference shader, RENDER_NS::RenderHandleReference graphics)
136 {
137 IRenderContext::Ptr context = context_.lock();
138 if (!context) {
139 CORE_LOG_E("Invalid context");
140 return false;
141 }
142 RENDER_NS::IShaderManager::IdDesc desc;
143 RENDER_NS::GraphicsState gs;
144 BASE_NS::string renderSlot;
145 context
146 ->AddTask([&] {
147 renderSlot = CORE3D_NS::DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE;
148 if (shader) {
149 desc = context->GetRenderer()->GetDevice().GetShaderManager().GetIdDesc(shader);
150 renderSlot = desc.renderSlot;
151 }
152 gs = CreateGraphicsState(context, graphics, renderSlot);
153 })
154 .Wait();
155
156 {
157 std::unique_lock lock { mutex_ };
158 handle_ = shader;
159 }
160
161 // todo: notify in same thread?
162 setShaderStateInProgress_ = true;
163 Name()->SetValue(desc.displayName);
164 CullMode()->SetValue(static_cast<CullModeFlags>(gs.rasterizationState.cullModeFlags));
165 Blend()->SetValue(IsBlendEnabled(gs));
166 setShaderStateInProgress_ = false;
167 UpdateGraphicsState(context, gs, renderSlot);
168 if (auto ev = EventOnResourceChanged(META_NS::MetadataQuery::EXISTING)) {
169 META_NS::Invoke<META_NS::IOnChanged>(ev);
170 }
171
172 return true;
173 }
174
OnPropertyChanged(const META_NS::IProperty & p)175 void Shader::OnPropertyChanged(const META_NS::IProperty& p)
176 {
177 if (setShaderStateInProgress_) {
178 // ignore the property sets during SetShaderState.
179 return;
180 }
181 auto context = context_.lock();
182 auto state = GetGraphicsState();
183 if (!context || !state) {
184 return;
185 }
186 if (p.GetName() == "CullMode") {
187 context
188 ->AddTask([&] {
189 auto gs = GetGraphicsState(context);
190 gs.rasterizationState.cullModeFlags = static_cast<RENDER_NS::CullModeFlags>(CullMode()->GetValue());
191 UpdateGraphicsState(context, gs);
192 })
193 .Wait();
194 } else if (p.GetName() == "Blend") {
195 context->AddTask([&] { UpdateGraphicsState(context, CreateNewGraphicsState(context, Blend()->GetValue())); })
196 .Wait();
197 }
198 }
199
200 SCENE_END_NAMESPACE()
201