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 <davidxli@google.com>
This commit is contained in:
David Li
2011-03-28 18:38:38 -07:00
parent 766c4e5a44
commit a60b6e69bf
9 changed files with 530 additions and 34 deletions

View File

@@ -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<Function, Button> buttonsBreak = new HashMap<Function, Button>();
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);
}
}
});

View File

@@ -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<Context> shares = new ArrayList<Context>(); // 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<GLProgram> programs = new ArrayList<GLProgram>();
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<Integer, GLShader> privateShaders = new HashMap<Integer, GLShader>();
HashMap<Integer, GLProgram> privatePrograms = new HashMap<Integer, GLProgram>();
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);
}
}
}

View File

@@ -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());

View File

@@ -301,4 +301,5 @@ public class MessageParserEx extends MessageParser {
// TODO: GLvoid* ptr
}
public final static MessageParserEx instance = new MessageParserEx();
}

View File

@@ -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<Message> 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<Message>());
incoming.put(contextId, new ArrayList<Message>());
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())

View File

@@ -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<MessageData> msgs = new ArrayList<MessageData>();
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);

View File

@@ -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<Message> cmds = new ArrayList<Message>();
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<Message> cmds = new ArrayList<Message>();
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++;
}
}
}