Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/shader/shader_file_editor_plugin.cpp
20936 views
1
/**************************************************************************/
2
/* shader_file_editor_plugin.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "shader_file_editor_plugin.h"
32
33
#include "editor/docks/editor_dock_manager.h"
34
#include "editor/editor_node.h"
35
#include "editor/editor_string_names.h"
36
#include "editor/settings/editor_command_palette.h"
37
#include "editor/themes/editor_scale.h"
38
#include "scene/gui/flow_container.h"
39
#include "scene/gui/item_list.h"
40
#include "scene/gui/split_container.h"
41
#include "servers/display/display_server.h"
42
43
/*** SHADER SCRIPT EDITOR ****/
44
45
/*** SCRIPT EDITOR ******/
46
47
void ShaderFileEditor::_update_version(const StringName &p_version_txt, const RD::ShaderStage p_stage) {
48
}
49
50
void ShaderFileEditor::_version_selected(int p_option) {
51
int c = versions->get_current();
52
StringName version_txt = versions->get_item_metadata(c);
53
54
RD::ShaderStage stage = RD::SHADER_STAGE_MAX;
55
int first_found = -1;
56
57
Ref<RDShaderSPIRV> bytecode = shader_file->get_spirv(version_txt);
58
ERR_FAIL_COND(bytecode.is_null());
59
60
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
61
if (bytecode->get_stage_bytecode(RD::ShaderStage(i)).is_empty() && bytecode->get_stage_compile_error(RD::ShaderStage(i)) == String()) {
62
stages[i]->set_button_icon(Ref<Texture2D>());
63
continue;
64
}
65
66
Ref<Texture2D> outcome_icon;
67
if (bytecode->get_stage_compile_error(RD::ShaderStage(i)) != String()) {
68
outcome_icon = get_editor_theme_icon(SNAME("ImportFail"));
69
} else {
70
outcome_icon = get_editor_theme_icon(SNAME("ImportCheck"));
71
}
72
stages[i]->set_button_icon(outcome_icon);
73
74
if (first_found == -1) {
75
first_found = i;
76
}
77
78
if (stages[i]->is_pressed()) {
79
stage = RD::ShaderStage(i);
80
break;
81
}
82
}
83
84
error_text->clear();
85
86
if (stage == RD::SHADER_STAGE_MAX) { //need to change stage, does not have it
87
if (first_found == -1) {
88
error_text->add_text(TTR("No valid shader stages found."));
89
return; //well you did not put any stage I guess?
90
}
91
stages[first_found]->set_pressed(true);
92
stage = RD::ShaderStage(first_found);
93
}
94
95
String error = bytecode->get_stage_compile_error(stage);
96
97
error_text->push_font(get_theme_font(SNAME("source"), EditorStringName(EditorFonts)));
98
99
if (error.is_empty()) {
100
error_text->add_text(TTR("Shader stage compiled without errors."));
101
} else {
102
error_text->add_text(error);
103
}
104
}
105
106
void ShaderFileEditor::_update_options() {
107
ERR_FAIL_COND(shader_file.is_null());
108
109
if (!shader_file->get_base_error().is_empty()) {
110
stage_hb->hide();
111
versions->hide();
112
error_text->clear();
113
error_text->push_font(get_theme_font(SNAME("source"), EditorStringName(EditorFonts)));
114
error_text->add_text(vformat(TTR("File structure for '%s' contains unrecoverable errors:\n\n"), shader_file->get_path().get_file()));
115
error_text->add_text(shader_file->get_base_error());
116
return;
117
}
118
119
stage_hb->show();
120
versions->show();
121
122
int c = versions->get_current();
123
//remember current
124
versions->clear();
125
TypedArray<StringName> version_list = shader_file->get_version_list();
126
127
if (c >= version_list.size()) {
128
c = version_list.size() - 1;
129
}
130
if (c < 0) {
131
c = 0;
132
}
133
134
StringName current_version;
135
136
for (int i = 0; i < version_list.size(); i++) {
137
String version_title = version_list[i];
138
if (version_title.is_empty()) {
139
version_title = "default";
140
}
141
142
Ref<Texture2D> outcome_icon;
143
144
Ref<RDShaderSPIRV> bytecode = shader_file->get_spirv(version_list[i]);
145
ERR_FAIL_COND(bytecode.is_null());
146
147
bool failed = false;
148
for (int j = 0; j < RD::SHADER_STAGE_MAX; j++) {
149
String error = bytecode->get_stage_compile_error(RD::ShaderStage(j));
150
if (!error.is_empty()) {
151
failed = true;
152
}
153
}
154
155
if (failed) {
156
outcome_icon = get_editor_theme_icon(SNAME("ImportFail"));
157
} else {
158
outcome_icon = get_editor_theme_icon(SNAME("ImportCheck"));
159
}
160
161
versions->add_item(version_title, outcome_icon);
162
versions->set_item_metadata(i, version_list[i]);
163
164
if (i == c) {
165
versions->select(i);
166
current_version = version_list[i];
167
}
168
}
169
170
if (version_list.is_empty()) {
171
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
172
stages[i]->set_disabled(true);
173
}
174
return;
175
}
176
177
Ref<RDShaderSPIRV> bytecode = shader_file->get_spirv(current_version);
178
ERR_FAIL_COND(bytecode.is_null());
179
int first_valid = -1;
180
int current = -1;
181
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
182
Vector<uint8_t> bc = bytecode->get_stage_bytecode(RD::ShaderStage(i));
183
String error = bytecode->get_stage_compile_error(RD::ShaderStage(i));
184
bool disable = error.is_empty() && bc.is_empty();
185
stages[i]->set_disabled(disable);
186
if (!disable) {
187
if (stages[i]->is_pressed()) {
188
current = i;
189
}
190
first_valid = i;
191
}
192
}
193
194
if (current == -1 && first_valid != -1) {
195
stages[first_valid]->set_pressed(true);
196
}
197
198
_version_selected(0);
199
}
200
201
void ShaderFileEditor::_notification(int p_what) {
202
switch (p_what) {
203
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
204
if (is_visible_in_tree() && shader_file.is_valid()) {
205
_update_options();
206
}
207
} break;
208
}
209
}
210
211
void ShaderFileEditor::_editor_settings_changed() {
212
if (is_visible_in_tree() && shader_file.is_valid()) {
213
_update_options();
214
}
215
}
216
217
void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) {
218
if (p_shader.is_null()) {
219
if (shader_file.is_valid()) {
220
shader_file->disconnect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed));
221
}
222
return;
223
}
224
225
if (shader_file == p_shader) {
226
return;
227
}
228
229
shader_file = p_shader;
230
231
if (shader_file.is_valid()) {
232
shader_file->connect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed));
233
}
234
235
_update_options();
236
}
237
238
void ShaderFileEditor::_shader_changed() {
239
if (is_visible_in_tree()) {
240
_update_options();
241
}
242
}
243
244
ShaderFileEditor *ShaderFileEditor::singleton = nullptr;
245
246
ShaderFileEditor::ShaderFileEditor() {
247
singleton = this;
248
249
set_name(TTRC("ShaderFile"));
250
set_icon_name("RDShaderFile");
251
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_shader_file_bottom_panel", TTRC("Toggle ShaderFile Dock")));
252
set_default_slot(EditorDock::DOCK_SLOT_BOTTOM);
253
set_available_layouts(EditorDock::DOCK_LAYOUT_ALL);
254
set_global(false);
255
set_transient(true);
256
set_custom_minimum_size(Size2(300, 200) * EDSCALE);
257
258
HSplitContainer *main_hs = memnew(HSplitContainer);
259
260
add_child(main_hs);
261
262
versions = memnew(ItemList);
263
versions->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
264
versions->connect(SceneStringName(item_selected), callable_mp(this, &ShaderFileEditor::_version_selected));
265
versions->set_custom_minimum_size(Size2i(100 * EDSCALE, 0));
266
versions->set_theme_type_variation("TreeSecondary");
267
main_hs->add_child(versions);
268
269
VBoxContainer *main_vb = memnew(VBoxContainer);
270
main_vb->set_h_size_flags(SIZE_EXPAND_FILL);
271
main_hs->add_child(main_vb);
272
273
static const char *stage_str[RD::SHADER_STAGE_MAX] = {
274
"Vertex",
275
"Fragment",
276
"TessControl",
277
"TessEval",
278
"Compute",
279
"Raygen",
280
"AnyHit",
281
"ClosestHit",
282
"Miss",
283
"Intersection",
284
};
285
286
stage_hb = memnew(HFlowContainer);
287
main_vb->add_child(stage_hb);
288
289
Ref<ButtonGroup> bg;
290
bg.instantiate();
291
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
292
Button *button = memnew(Button(stage_str[i]));
293
button->set_toggle_mode(true);
294
button->set_focus_mode(FOCUS_ACCESSIBILITY);
295
stage_hb->add_child(button);
296
stages[i] = button;
297
button->set_button_group(bg);
298
button->connect(SceneStringName(pressed), callable_mp(this, &ShaderFileEditor::_version_selected).bind(i));
299
}
300
301
error_text = memnew(RichTextLabel);
302
error_text->set_v_size_flags(SIZE_EXPAND_FILL);
303
error_text->set_selection_enabled(true);
304
error_text->set_context_menu_enabled(true);
305
main_vb->add_child(error_text);
306
}
307
308
void ShaderFileEditorPlugin::edit(Object *p_object) {
309
RDShaderFile *s = Object::cast_to<RDShaderFile>(p_object);
310
shader_editor->edit(s);
311
}
312
313
bool ShaderFileEditorPlugin::handles(Object *p_object) const {
314
RDShaderFile *shader = Object::cast_to<RDShaderFile>(p_object);
315
return shader != nullptr;
316
}
317
318
void ShaderFileEditorPlugin::make_visible(bool p_visible) {
319
if (p_visible) {
320
shader_editor->make_visible();
321
} else {
322
shader_editor->close();
323
}
324
}
325
326
ShaderFileEditorPlugin::ShaderFileEditorPlugin() {
327
shader_editor = memnew(ShaderFileEditor);
328
EditorDockManager::get_singleton()->add_dock(shader_editor);
329
shader_editor->close();
330
}
331
332