From a60b6e69bf3cb1d6b1b6e8c6f25396a89ca60a86 Mon Sep 17 00:00:00 2001 From: David Li Date: Mon, 28 Mar 2011 18:38:38 -0700 Subject: [PATCH] GLES2Dbg: implemented shader tracking and editing Next commit is improving the protocol and checking errors after shader upload Change-Id: I6afe3b63a68e00cd395885fd26cd6fcb311cfbec Signed-off-by: David Li --- .../glesv2debugger/BreakpointOption.java | 6 +- .../com/android/glesv2debugger/Context.java | 21 +- .../glesv2debugger/DebuggerMessage.java | 6 +- .../glesv2debugger/GLServerShader.java | 206 +++++++++++++++ .../android/glesv2debugger/MessageData.java | 8 +- .../glesv2debugger/MessageParserEx.java | 1 + .../android/glesv2debugger/MessageQueue.java | 35 ++- .../android/glesv2debugger/SampleView.java | 41 ++- .../android/glesv2debugger/ShaderEditor.java | 240 ++++++++++++++++++ 9 files changed, 530 insertions(+), 34 deletions(-) create mode 100644 tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java create mode 100644 tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java b/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java index bf5c40de2..64cec744a 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/BreakpointOption.java @@ -21,7 +21,6 @@ import com.android.glesv2debugger.DebuggerMessage.Message.Function; import com.android.glesv2debugger.DebuggerMessage.Message.Prop; import com.android.glesv2debugger.DebuggerMessage.Message.Type; import org.eclipse.jface.dialogs.InputDialog; -import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; @@ -42,7 +41,6 @@ public class BreakpointOption extends ScrolledComposite implements SelectionList SampleView sampleView; HashMap buttonsBreak = new HashMap(); - MessageParserEx messageParserEx = new MessageParserEx(); BreakpointOption(SampleView sampleView, Composite parent) { super(parent, SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL); @@ -72,7 +70,6 @@ public class BreakpointOption extends ScrolledComposite implements SelectionList this.setExpandVertical(true); this.setMinSize(size); this.layout(); - // this.pack(true); } void SetBreakpoint(Function function, boolean enabled) { @@ -140,7 +137,8 @@ public class BreakpointOption extends ScrolledComposite implements SelectionList SetBreakpoint(msg.getFunction(), false); } else - messageParserEx.Parse(builder, inputDialog.getValue()); + MessageParserEx.instance.Parse(builder, inputDialog.getValue()); + builder.setExpectResponse(true); } } }); diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java index 3668a5614..54ddc9a10 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/Context.java @@ -18,13 +18,21 @@ package com.android.glesv2debugger; import com.android.glesv2debugger.DebuggerMessage.Message; -public class Context { - public int contextId; - public GLServerVertex serverVertex = new GLServerVertex(); - public byte [] readPixelRef = new byte [0]; +import java.util.ArrayList; - public Message ProcessMessage(Message msg) - { +public class Context { + public final int contextId; + ArrayList shares = new ArrayList(); // includes self + public GLServerVertex serverVertex = new GLServerVertex(); + public GLServerShader serverShader = new GLServerShader(this); + public byte[] readPixelRef = new byte[0]; + + public Context(int contextId) { + this.contextId = contextId; + shares.add(this); + } + + public Message ProcessMessage(Message msg) { switch (msg.getFunction()) { case glBindBuffer: serverVertex.glBindBuffer(msg); @@ -83,6 +91,7 @@ public class Context { serverVertex.glVertexAttrib4fv(msg); break; } + serverShader.ProcessMessage(msg); return msg; } } diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java b/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java index ce20caf37..9b8c61058 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/DebuggerMessage.java @@ -446,7 +446,8 @@ public final class DebuggerMessage { implements com.google.protobuf.Internal.EnumLite { BeforeCall(0, 0), AfterCall(1, 1), - Response(2, 2), + AfterGeneratedCall(2, 2), + Response(3, 3), ; @@ -456,7 +457,8 @@ public final class DebuggerMessage { switch (value) { case 0: return BeforeCall; case 1: return AfterCall; - case 2: return Response; + case 2: return AfterGeneratedCall; + case 3: return Response; default: return null; } } diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java new file mode 100644 index 000000000..281db68de --- /dev/null +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/GLServerShader.java @@ -0,0 +1,206 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +package com.android.glesv2debugger; + +import com.android.glesv2debugger.DebuggerMessage.Message; + +import java.util.ArrayList; +import java.util.HashMap; + +class GLShader { + final int name; + final GLServerShader context; // the context this was created in + final GLEnum type; + boolean delete; + ArrayList programs = new ArrayList(); + String source, originalSource; + + GLShader(final int name, final GLServerShader context, final GLEnum type) { + this.name = name; + this.context = context; + this.type = type; + } +} + +class GLProgram { + final int name; + final GLServerShader context; // the context this was created in + boolean delete; + GLShader vert, frag; + + GLProgram(final int name, final GLServerShader context) { + this.name = name; + this.context = context; + } +} + +public class GLServerShader { + final Context context; + HashMap privateShaders = new HashMap(); + HashMap privatePrograms = new HashMap(); + GLProgram current = null; + public boolean uiUpdate = false; + + GLServerShader(final Context context) { + this.context = context; + } + + public void ProcessMessage(final Message msg) { + boolean oldUiUpdate = uiUpdate; + uiUpdate = true; + switch (msg.getFunction()) { + case glAttachShader: + glAttachShader(msg); + break; + case glCreateProgram: + glCreateProgram(msg); + break; + case glCreateShader: + glCreateShader(msg); + break; + case glDeleteProgram: + glDeleteProgram(msg); + break; + case glDeleteShader: + glDeleteShader(msg); + break; + case glDetachShader: + glDetachShader(msg); + break; + case glShaderSource: + glShaderSource(msg); + break; + case glUseProgram: + glUseProgram(msg); + break; + default: + uiUpdate = oldUiUpdate; + break; + } + } + + GLShader GetShader(int name) { + if (name == 0) + return null; + for (Context ctx : context.shares) { + GLShader shader = ctx.serverShader.privateShaders.get(name); + if (shader != null) + return shader; + } + assert false; + return null; + } + + GLProgram GetProgram(int name) { + if (name == 0) + return null; + for (Context ctx : context.shares) { + GLProgram program = ctx.serverShader.privatePrograms.get(name); + if (program != null) + return program; + } + assert false; + return null; + } + + // void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) + void glAttachShader(final Message msg) { + GLProgram program = GetProgram(msg.getArg0()); + GLShader shader = GetShader(msg.getArg1()); + if (GLEnum.GL_VERTEX_SHADER == shader.type) + program.vert = shader; + else + program.frag = shader; + shader.programs.add(program); + } + + // GLuint API_ENTRY(glCreateProgram)(void) + void glCreateProgram(final Message msg) { + privatePrograms.put(msg.getRet(), new GLProgram(msg.getRet(), this)); + } + + // GLuint API_ENTRY(glCreateShader)(GLenum type) + void glCreateShader(final Message msg) { + privateShaders.put(msg.getRet(), + new GLShader(msg.getRet(), this, GLEnum.valueOf(msg.getArg0()))); + } + + // void API_ENTRY(glDeleteProgram) + void glDeleteProgram(final Message msg) { + if (msg.getArg0() == 0) + return; + GLProgram program = GetProgram(msg.getArg0()); + program.delete = true; + for (Context ctx : context.shares) + if (ctx.serverShader.current == program) + return; + glDetachShader(program, program.vert); + glDetachShader(program, program.frag); + privatePrograms.remove(program.name); + } + + // void API_ENTRY(glDeleteShader)(GLuint shader) + void glDeleteShader(final Message msg) { + if (msg.getArg0() == 0) + return; + GLShader shader = GetShader(msg.getArg0()); + shader.delete = true; + if (shader.programs.size() == 0) + privateShaders.remove(shader.name); + } + + // void API_ENTRY(glDetachShader)(GLuint program, GLuint shader) + void glDetachShader(final Message msg) { + glDetachShader(GetProgram(msg.getArg0()), GetShader(msg.getArg1())); + } + + void glDetachShader(final GLProgram program, final GLShader shader) { + if (program == null) + return; + if (program.vert == shader) + program.vert = null; + else if (program.frag == shader) + program.frag = null; + else + return; + shader.programs.remove(program); + if (shader.delete && shader.programs.size() == 0) + shader.context.privateShaders.remove(shader.name); + } + + // void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const + // GLchar** string, const GLint* length) + void glShaderSource(final Message msg) { + if (!msg.hasData()) + return; // TODO: distinguish between generated calls + GLShader shader = GetShader(msg.getArg0()); + shader.source = shader.originalSource = msg.getData().toStringUtf8(); + } + + // void API_ENTRY(glUseProgram)(GLuint program) + void glUseProgram(final Message msg) { + GLProgram oldCurrent = current; + current = GetProgram(msg.getArg0()); + if (null != oldCurrent && oldCurrent.delete && oldCurrent != current) + { + for (Context ctx : context.shares) + if (ctx.serverShader.current == oldCurrent) + return; + oldCurrent.context.privatePrograms.remove(oldCurrent.name); + } + } +} diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java index df5cbed2e..9c4143d33 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageData.java @@ -19,6 +19,7 @@ package com.android.glesv2debugger; import com.android.glesv2debugger.DebuggerMessage.Message; import com.android.glesv2debugger.DebuggerMessage.Message.DataType; import com.android.glesv2debugger.DebuggerMessage.Message.Function; +import com.android.glesv2debugger.DebuggerMessage.Message.Type; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Image; @@ -51,11 +52,16 @@ public class MessageData { builder.append(String.format(":%.3f", msg.getClock())); builder.append(String.format(" 0x%08X", msg.getContextId())); builder.append(" "); + if (msg.getType() == Type.BeforeCall) // incomplete call, client SKIPPED + builder.append("[BeforeCall(AfterCall missing)] "); + else if (msg.getType() == Type.AfterGeneratedCall) + builder.append("[AfterGeneratedCall] "); + else + assert msg.getType() == Type.AfterCall; builder.append(MessageFormatter.Format(msg)); switch (function) { case glDrawArrays: // msg was modified by GLServerVertex case glDrawElements: - assert msg.hasData(); if (!msg.hasArg8() || !msg.hasData()) break; dataType = GLEnum.valueOf(msg.getArg8()); diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java index 93b9e1bb1..a466ef86e 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageParserEx.java @@ -301,4 +301,5 @@ public class MessageParserEx extends MessageParser { // TODO: GLvoid* ptr } + public final static MessageParserEx instance = new MessageParserEx(); } diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java index fcace8e2c..180477988 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/MessageQueue.java @@ -20,10 +20,6 @@ import com.android.glesv2debugger.DebuggerMessage.Message; import com.android.glesv2debugger.DebuggerMessage.Message.Function; import com.android.glesv2debugger.DebuggerMessage.Message.Type; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Shell; - import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; @@ -85,6 +81,12 @@ public class MessageQueue implements Runnable { } } + public void AddCommands(ArrayList cmds) { + synchronized (commands) { + commands.addAll(cmds); + } + } + @Override public void run() { Socket socket = new Socket(); @@ -101,7 +103,6 @@ public class MessageQueue implements Runnable { Error(e); } - // try { while (running) { Message msg = null; if (incoming.size() > 0) { // find queued incoming @@ -126,8 +127,10 @@ public class MessageQueue implements Runnable { int contextId = msg.getContextId(); if (!incoming.containsKey(contextId)) - incoming.put(contextId, - new ArrayList()); + incoming.put(contextId, new ArrayList()); + + if (msg.getType() == Type.AfterGeneratedCall) + continue; // TODO: for now, don't care // FIXME: the expected sequence will change for interactive mode while (msg.getType() == Type.BeforeCall) { @@ -149,6 +152,9 @@ public class MessageQueue implements Runnable { break; } + if (next.getType() == Type.AfterGeneratedCall) + continue; // TODO: for now, don't care + if (next.getContextId() != contextId) { // message part not for this context if (!incoming.containsKey(next.getContextId())) @@ -176,13 +182,14 @@ public class MessageQueue implements Runnable { Error(e); running = false; } - // } catch (Exception e) { - // Error(e); - // running = false; - // } } - public Message RemoveMessage(int contextId) { + Message GetMessage(int contextId) { + // ReadMessage and filter by contextId + return null; + } + + public Message RemoveCompleteMessage(int contextId) { synchronized (complete) { if (complete.size() == 0) return null; @@ -244,10 +251,12 @@ public class MessageQueue implements Runnable { builder.setFunction(Function.CONTINUE); else if (msg.getType() == Type.AfterCall) builder.setFunction(Function.SKIP); + else if (msg.getType() == Type.AfterGeneratedCall) + builder.setFunction(Function.SKIP); else assert false; builder.setType(Type.Response); - builder.setExpectResponse(false); + builder.setExpectResponse(msg.getExpectResponse()); if (msg.getExpectResponse()) sampleView.breakpointOption.BreakpointReached(builder, msg); if (SendCommands(dos, 0) || msg.getExpectResponse()) diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java index c515339ab..adce6340a 100644 --- a/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/SampleView.java @@ -103,6 +103,7 @@ public class SampleView extends ViewPart implements Runnable { LayoutComposite layoutComposite; ListViewer viewer; BreakpointOption breakpointOption; + ShaderEditor shaderEditor; org.eclipse.swt.widgets.Canvas canvas; Text text; Action actionConnect; // connect / disconnect @@ -239,11 +240,6 @@ public class SampleView extends ViewPart implements Runnable { // Create the help context id for the viewer's control PlatformUI.getWorkbench().getHelpSystem() .setHelp(viewer.getControl(), "GLESv2DebuggerClient.viewer"); - makeActions(); - hookContextMenu(); - hookDoubleClickAction(); - hookSelectionChanged(); - contributeToActionBars(); layoutComposite = new LayoutComposite(parent, 0); layoutComposite.setLayout(new FillLayout()); @@ -257,6 +253,7 @@ public class SampleView extends ViewPart implements Runnable { canvas.setVisible(false); breakpointOption = new BreakpointOption(this, layoutComposite); + shaderEditor = new ShaderEditor(this, layoutComposite); final ScrollBar hBar = canvas.getHorizontalBar(); hBar.addListener(SWT.Selection, new Listener() { @@ -335,6 +332,12 @@ public class SampleView extends ViewPart implements Runnable { } } }); + + makeActions(); + hookContextMenu(); + hookDoubleClickAction(); + hookSelectionChanged(); + contributeToActionBars(); } private void hookContextMenu() { @@ -465,7 +468,18 @@ public class SampleView extends ViewPart implements Runnable { { breakpointOption.setVisible(!breakpointOption.isVisible()); layoutComposite.layout(true); - manager.update(true); + } + }; + action.setChecked(true); + manager.add(action); + + action = new Action("Shaders", Action.AS_CHECK_BOX) + { + @Override + public void run() + { + shaderEditor.setVisible(!shaderEditor.isVisible()); + layoutComposite.layout(true); } }; action.setChecked(true); @@ -606,14 +620,24 @@ public class SampleView extends ViewPart implements Runnable { showError(e1); } ArrayList msgs = new ArrayList(); + boolean shaderEditorUpdate = false; while (running) { if (!messageQueue.IsRunning()) break; - Message msg = messageQueue.RemoveMessage(0); + Message msg = messageQueue.RemoveCompleteMessage(0); if (msgs.size() > 60 || (msgs.size() > 0 && null == msg)) { viewContentProvider.add(msgs); msgs.clear(); + + if (shaderEditorUpdate) + this.getSite().getShell().getDisplay().syncExec(new Runnable() { + @Override + public void run() { + shaderEditor.Update(); + } + }); + shaderEditorUpdate = false; } if (null == msg) { try { @@ -626,10 +650,11 @@ public class SampleView extends ViewPart implements Runnable { Context context = contexts.get(msg.getContextId()); if (null == context) { - context = new Context(); + context = new Context(msg.getContextId()); contexts.put(msg.getContextId(), context); } msg = context.ProcessMessage(msg); + shaderEditorUpdate |= context.serverShader.uiUpdate; final MessageData msgData = new MessageData(this.getViewSite() .getShell().getDisplay(), msg, context); diff --git a/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java new file mode 100644 index 000000000..ef10273e2 --- /dev/null +++ b/tools/glesv2debugger/src/com/android/glesv2debugger/ShaderEditor.java @@ -0,0 +1,240 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +package com.android.glesv2debugger; + +import com.android.glesv2debugger.DebuggerMessage.Message; +import com.android.glesv2debugger.DebuggerMessage.Message.Type; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ExtendedModifyEvent; +import org.eclipse.swt.custom.ExtendedModifyListener; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; + +import java.util.ArrayList; + +public class ShaderEditor extends Composite implements SelectionListener, ExtendedModifyListener { + SampleView sampleView; + + ToolBar toolbar; + ToolItem uploadShader, restoreShader; + List list; + StyledText styledText; + + GLShader current; + + ArrayList cmds = new ArrayList(); + + ShaderEditor(SampleView sampleView, Composite parent) { + super(parent, 0); + this.sampleView = sampleView; + + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 1; + this.setLayout(gridLayout); + + toolbar = new ToolBar(this, SWT.BORDER); + + uploadShader = new ToolItem(toolbar, SWT.PUSH); + uploadShader.setText("Upload Shader"); + uploadShader.addSelectionListener(this); + + restoreShader = new ToolItem(toolbar, SWT.PUSH); + restoreShader.setText("Original Shader"); + restoreShader.addSelectionListener(this); + + list = new List(this, SWT.V_SCROLL); + list.setFont(new Font(parent.getDisplay(), "Courier", 10, 0)); + list.addSelectionListener(this); + GridData gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.grabExcessHorizontalSpace = true; + gridData.verticalAlignment = SWT.FILL; + gridData.grabExcessVerticalSpace = true; + list.setLayoutData(gridData); + + styledText = new StyledText(this, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI); + gridData = new GridData(); + gridData.horizontalAlignment = SWT.FILL; + gridData.grabExcessHorizontalSpace = true; + gridData.verticalAlignment = SWT.FILL; + gridData.grabExcessVerticalSpace = true; + styledText.setLayoutData(gridData); + styledText.addExtendedModifyListener(this); + } + + public void Update() { + list.removeAll(); + for (Context context : sampleView.contexts.values()) { + for (GLShader shader : context.serverShader.privateShaders.values()) { + StringBuilder builder = new StringBuilder(); + builder.append(String.format("%08X", context.contextId)); + builder.append(' '); + builder.append(shader.type); + while (builder.length() < 30) + builder.append(" "); + builder.append(shader.name); + while (builder.length() < 40) + builder.append(" "); + builder.append(':'); + for (Context ctx : context.shares) { + builder.append(String.format("%08X", ctx.contextId)); + builder.append(' '); + } + builder.append(':'); + for (GLProgram program : shader.programs) { + builder.append(program.name); + builder.append(" "); + } + list.add(builder.toString()); + } + } + } + + void UploadShader() { + current.source = styledText.getText(); + + ArrayList cmds = new ArrayList(); + final int contextId = current.context.context.contextId; + + Message.Builder builder = GetBuilder(contextId); + MessageParserEx.instance.Parse(builder, + String.format("glShaderSource(%d,1,\"%s\",0)", current.name, current.source)); + cmds.add(builder.build()); + + builder = GetBuilder(contextId); + MessageParserEx.instance.Parse(builder, + String.format("glCompileShader(%d)", current.name)); + cmds.add(builder.build()); + + for (GLProgram program : current.programs) { + builder = GetBuilder(contextId); + MessageParserEx.instance.Parse(builder, + String.format("glLinkProgram(%d)", program.name)); + cmds.add(builder.build()); + } + + sampleView.messageQueue.AddCommands(cmds); + } + + Message.Builder GetBuilder(int contextId) { + Message.Builder builder = Message.newBuilder(); + builder.setContextId(contextId); + builder.setType(Type.Response); + builder.setExpectResponse(false); + return builder; + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.getSource() == uploadShader && null != current) { + UploadShader(); + return; + } else if (e.getSource() == restoreShader && null != current) { + current.source = styledText.getText(); + styledText.setText(current.originalSource); + return; + } + + if (list.getSelectionCount() < 1) + return; + if (null != current && !current.source.equals(styledText.getText())) { + String[] btns = { + "&Upload", "&Save", "&Discard" + }; + MessageDialog dialog = new MessageDialog(this.getShell(), "Shader Edited", + null, "Shader source has been edited", MessageDialog.QUESTION, btns, 0); + int rc = dialog.open(); + if (rc == SWT.DEFAULT || rc == 0) + UploadShader(); + else if (rc == 1) + current.source = styledText.getText(); + // else if (rc == 2) do nothing; selection is changing + } + String[] details = list.getSelection()[0].split("\\s+"); + final int contextId = Integer.parseInt(details[0], 16); + int name = Integer.parseInt(details[2]); + current = sampleView.contexts.get(contextId).serverShader.privateShaders.get(name); + styledText.setText(current.source); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + widgetSelected(e); + } + + @Override + public void modifyText(ExtendedModifyEvent event) { + final String[] keywords = { + "gl_Position", "gl_FragColor" + }; + // FIXME: proper scanner for syntax highlighting + String text = styledText.getText(); + int start = event.start; + int end = event.start + event.length; + start -= 20; // deleting chars from keyword causes rescan + end += 20; + if (start < 0) + start = 0; + if (end > text.length()) + end = text.length(); + if (null != styledText.getStyleRangeAtOffset(event.start)) { + StyleRange clearStyleRange = new StyleRange(); + clearStyleRange.start = start; + clearStyleRange.length = end - start; + clearStyleRange.foreground = event.display.getSystemColor(SWT.COLOR_BLACK); + styledText.setStyleRange(clearStyleRange); + } + + while (start < end) { + for (final String keyword : keywords) { + if (!text.substring(start).startsWith(keyword)) + continue; + if (start > 0) { + final char before = text.charAt(start - 1); + if (Character.isLetterOrDigit(before)) + continue; + else if (before == '_') + continue; + } + if (start + keyword.length() < text.length()) { + final char after = text.charAt(start + keyword.length()); + if (Character.isLetterOrDigit(after)) + continue; + else if (after == '_') + continue; + } + StyleRange style1 = new StyleRange(); + style1.start = start; + style1.length = keyword.length(); + style1.foreground = event.display.getSystemColor(SWT.COLOR_BLUE); + styledText.setStyleRange(style1); + } + start++; + } + } +}